设计模式-- 原型模式详解

原型模式(prototype)

原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象,原型模式属于创造性模式,它同样提供了创建对象的最佳方式之一。(效率很高

原型模式实现了一个原型接口,该接口用于创建当前对象的克隆,当创建的对象过于复杂,代价较大的时候,使用原型模式,

例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

原型模式的实现

Java提供了对象的clone()方法,实现原型模式只需要实现Cloneable接口,重写clone()方法

原型模式包括:

  • 抽象原型类:规定了具体原型对象必须实现的接口。

  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。

  • 客户端:使用具体原型类中的 clone() 方法来复制新的对象。

代码展示:

具体原型类:

java 复制代码
 package com.lyc.prototype.demo1;
 ​
 import lombok.*;
 ​
 import java.util.Date;
 /*
 * 1.实现一个接口 Cloneable
 * 2,重写一个方法 clone()
 * */
 //视频的模型
 @Getter
 @Setter
 @ToString
 @AllArgsConstructor
 @NoArgsConstructor
 public class Video implements Cloneable{
     //抄袭者,抄别人的视频
     private String name;
     private Date createTime;
 ​
     @Override
     protected Object clone() throws CloneNotSupportedException {
         return super.clone();
     }
 }

客户端:

java 复制代码
 package com.lyc.prototype.demo1;
 ​
 import java.util.Date;
 ​
 //客户端
 public class Client {
     public static void main(String[] args) throws Exception {
         //原型对象 video
         //浅克隆 video和video2的createTime是同一个对象,date修改,video2也会变
         Date date = new Date();
         Video video = new Video("设计模式之原型模式",date);
         Video video2 = (Video) video.clone();
         System.out.println("video:"+video);
         System.out.println("video2:"+video2);
         date.setTime(7756796);
         System.out.println("video:"+video);
         System.out.println("video2:"+video2);
 ​
 //        video:Video(name=设计模式之原型模式, createTime=Fri Apr 25 16:55:46 CST 2025)
 //        hashCode:284720968
 //        video2:Video(name=设计模式之原型模式, createTime=Fri Apr 25 16:55:46 CST 2025)
 //        hashCode:2093176254
     }
     /*
     *   System.out.println("video:"+video);
         System.out.println("hashCode:"+video.hashCode());
         //video 克隆 video2 克隆出来的对象和原来是一模一样的
         System.out.println("video2:"+video2);
         System.out.println("hashCode:"+video2.hashCode());
     * */
 }
拓展知识

native

Java关键字,是用来说明该方法是原生函数,即这个方法使用C/C++语言实现的,并且被编译成了DLL,由Java调用。

native的意思就是通知操作系统,这个函数必须实现,所以native关键字的函数都是操作系统实现的,Java只能调用。

java是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制 ,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了

Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。

可以将native方法比作Java程序同C程序的接口,其实现步骤:

  1. 在Java中声明native()方法,然后编译;
  2. 用javah产生一个.h文件;
  3. 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
  4. 将第三步的.cpp文件编译成动态链接库文件;
  5. 在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

原型模式优点

  • 性能优良:原型模式是使用native本地方法clone(),直接从内存中二进制流的拷贝,要比new一个对象性能好很多,特别是在一个循环体类产生大量对象的时候更加明显。

  • 逃避构造函数的约束:这是优缺点共存的一点,直接在内存中拷贝,构造函数是不会执行的

缺点:

  • 配备克隆方法需要全面考虑类的功能,对已有类可能较难实现,特别是处理不支持串行化的间接对象或含有循环结构的引用时。

  • 必须实现**Cloneable**接口

应用实例

JavaScript对象的继承就是使用原型链来完成的。

Spring中Bean的创建分为单例模式,原型模式。

适用场景

  • 资源优化(直接调用本地方法,节省资源,效率较高)

  • 类初始化需要消耗大量资源(如数据、硬件资源)

  • 性能和安全要求高的场景(直接从底层调用方法,性能很高,clone方法直接拷贝对象,安全性高)

  • 通过 new 创建对象需要复杂的数据准备或访问权限时

  • 一个对象需要多个修改者

  • 对象需提供给其他对象访问并可能被各个调用者修改时

  • 通常与工厂方法模式一起使用,通过 clone 创建对象,然后由工厂方法提供给调用者

拓展:浅拷贝 与 深拷贝

浅拷贝:在clone之后,两个对象共用一个私有属性,该属性性改变则两个对象一块改变

原因:object类的clone方法只拷贝本对象,其对象内部的数组,引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就是浅拷贝。两个对象共用一个私有变量。这是一种非常不安全的方式。

原始类型会被拷贝(int,double,long。。。),String类型也会被拷贝;数组、引用类型不会被拷贝

代码展示:

java 复制代码
 public static void main(String[] args) throws Exception {
     //原型对象 video
     //浅克隆 video和video2的createTime是同一个对象,date修改,video2也会变
     Date date = new Date();
     Video video = new Video("设计模式之原型模式",date);
     Video video2 = (Video) video.clone();
     System.out.println("video:"+video);
     System.out.println("video2:"+video2);
     date.setTime(7756796);
      System.out.println("==============================================");
     System.out.println("video:"+video);
     System.out.println("video2:"+video2);

效果展示:

复制代码
 video:Video(name=设计模式之原型模式, createTime=Fri Apr 25 17:50:08 CST 2025)
 video2:Video(name=设计模式之原型模式, createTime=Fri Apr 25 17:50:08 CST 2025)
 ==============================================
 video:Video(name=设计模式之原型模式, createTime=Thu Jan 01 10:09:16 CST 1970)
 video2:Video(name=设计模式之原型模式, createTime=Thu Jan 01 10:09:16 CST 1970)

深拷贝:在clone之后,两个对象的属性也被克隆,不再共用

如何实现?

  • 方式一:重写clone方法,将对象的属性也克隆一份

  • 方式二:通过实现 Serializable 读取二进制流实现。

方式一实现方式:

java 复制代码
 package com.lyc.prototype.demo2;
 ​
 import lombok.*;
 ​
 import java.util.Date;
 /*
 * 1.实现一个接口 Cloneable
 * 2,重写一个方法 clone()
 * */
 //视频的模型
 @Getter
 @Setter
 @ToString
 @AllArgsConstructor
 @NoArgsConstructor
 public class Video implements Cloneable{
     //抄袭者,抄别人的视频
     private String name;
     private Date createTime;
 ​
     @Override
     protected Object clone() throws CloneNotSupportedException {
         //实现深克隆的方式一,方式二: 序列化,反序列化
         Object clone = super.clone();
          Video v = (Video)clone;
          //将该对象的属性也克隆
         v.createTime = (Date) createTime.clone();
         return clone;
     }
 }
java 复制代码
 public class Client {
     public static void main(String[] args) throws Exception {
         //原型对象 video
         //浅克隆 video和video2的createTime是同一个对象,date修改,video2也会变
         Date date = new Date();
         Video video = new Video("设计模式之原型模式",date);
         Video video2 = (Video) video.clone();
         System.out.println("video:"+video);
         System.out.println("video2:"+video2);
         date.setTime(7756796);
         System.out.println("==============================================");
         System.out.println("video:"+video);
         System.out.println("video2:"+video2);

效果展示:

复制代码
 video:Video(name=设计模式之原型模式, createTime=Fri Apr 25 17:54:20 CST 2025)
 video2:Video(name=设计模式之原型模式, createTime=Fri Apr 25 17:54:20 CST 2025)
 ==============================================
 video:Video(name=设计模式之原型模式, createTime=Thu Jan 01 10:09:16 CST 1970)
 video2:Video(name=设计模式之原型模式, createTime=Fri Apr 25 17:54:20 CST 2025)

注意事项:

  • 构造方法在clone的时候并不会执行,因为对象是从内存以二进制流的方式进行拷贝,当然不会执行

  • 深拷贝和浅拷贝要分开实现,不然会导致程序变得非常复杂

  • 带有final类型的变量是不可以进行拷贝的,这样是无法实现深拷贝

这是因为final关键字的特性

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。因此:要使用clone()方法,类的成员变量上不要增加final关键字。

小结:

使用原型类型时,引用的成员变量必须满足两个条件才不会被拷贝:

  • 是类的成员变量,而不是方法内的变量

  • 必须是一个可变的引用对象,而不是一个原始类型或者不可变对象(比如final)

以上就是我对原型模式的理解,希望对大家有所帮助

相关推荐
晨米酱1 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机6 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机7 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机7 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机7 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤8 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式