五指山市网站建设_网站建设公司_jQuery_seo优化
2025/12/27 3:26:08 网站建设 项目流程

文章目录

  • 0. 个人感悟
  • 1. 概念
  • 2. 适配场景(什么场景下使用)
  • 3. 实现方法(实现的思路)
    • 4. 代码示例
    • 4.1 传统方式
    • 4.2 原型模式
  • 5. 浅拷贝和深拷贝
    • 5.1 概念
    • 5.2 浅拷贝示例
    • 5.3 深拷贝实现1-重新clone方法,自己控制属性深拷贝(不推荐)
    • 5.4 深拷贝实现2-序列化(推荐)
  • 6. 原型模式优缺点

0. 个人感悟

  • 原型模式主要针对对象的复制场景,能够屏蔽复制的细节,对外提供复制能力
  • 很多场景下创建对象需要查库、计算等操作,重新new非常耗时
  • 原型模式的实现很简单,对java而言,实现Cloneable接口,重写clone()方法
  • 注意业务场景是需要深拷贝还是浅拷贝
  • 原型模式单独使用场景少,通常与其它模式一起使用,用于动态配置对象

1. 概念

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

翻译:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
理解:

  • 核心思想: 不是通过new关键字直接创建对象,而是通过“克隆”一个已存在的实例来创建新的对象

2. 适配场景(什么场景下使用)

  • 创建成本高的对象: 比如当一个对象的初始化需要消耗大量资源(数据库加载,复杂计算等),且需要创建多个相似对象
  • 避免构造函数的约束: 原型模式复制对象不会调用构造函数,因此可以跳过构造函数的约束。所以这一点可以跳过某些单例模式实现,需要注意。
  • 需要动态性的配置对象: 当系统需要根据运行时的状态创建对象,而这些对象知识某个原型的变体时。
  • 保护性拷贝: 需要创建对象的副本来避免原始对象被意外修改。当然这里注意是深拷贝,后面会讨论。

3. 实现方法(实现的思路)

  • 抽象原型类: 规定原型对象必须实现的接口。jdk中已经定义了Cloneable接口,用于标识这个类可以安全的进行复制,否则运行时会抛出CloneNotSupported异常
  • 具体抽象类: 实现原型类的clone()方法。对java而言是重写object类的clone方法,注意其作用域 protected,一般的类无法调用,重写时注意将 clone() 方法的作用域修改为 public。
    在JDK中可以看到,Cloneable接口仅是一个标识:
packagejava.lang;/** * A class implements the {@code Cloneable} interface to * indicate to the {@link java.lang.Object#clone()} method that it * is legal for that method to make a * field-for-field copy of instances of that class. * <p> * Invoking Object's clone method on an instance that does not implement the * {@code Cloneable} interface results in the exception * {@code CloneNotSupportedException} being thrown. * <p> * By convention, classes that implement this interface should override * {@code Object.clone} (which is protected) with a public method. * See {@link java.lang.Object#clone()} for details on overriding this * method. * <p> * Note that this interface does <i>not</i> contain the {@code clone} method. * Therefore, it is not possible to clone an object merely by virtue of the * fact that it implements this interface. Even if the clone method is invoked * reflectively, there is no guarantee that it will succeed. * * @see java.lang.CloneNotSupportedException * @see java.lang.Object#clone() * @since 1.0 */publicinterfaceCloneable{}

说明部分的翻译:

实现 Cloneable 接口的类会向 Object.clone() 方法表明,该方法可以对该类的实例进行字段级复制。

如果对未实现 Cloneable 接口的实例调用 Object 的 clone 方法,则会抛出 CloneNotSupportedException 异常。

按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(该方法为受保护方法)。有关重写此方法的详细信息,请参阅 Object.clone() 的文档。

请注意,此接口本身并不包含 clone 方法。因此,仅仅因为对象实现了此接口,并不能克隆该对象。即使通过反射调用 clone 方法,也不能保证克隆成功。

类图:

  • Prototype: 原型类,声明克隆接口
  • Realizetype: 具体原型,实现原型接口,可复制。
  • Client: 访问类

4. 代码示例

送请柬业务:
当需要给大量客人发送请柬,每份请柬只有personName不同,而messageaddress基本相同时。
不需要为每位客人重复构建完整的请柬对象,只需克隆并修改姓名即可。

4.1 传统方式

po

publicclassInvitation{StringpersonName;Stringmessage;Addressaddress;publicInvitation(StringpersonName,Stringmessage){this.personName=personName;this.message=message;}publicInvitation(StringpersonName,Stringmessage,Addressaddress){this.personName=personName;this.message=message;this.address=address;}// 省略}publicclassAddress{Stringstreet;Stringcity;publicAddress(Stringstreet,Stringcity){this.street=street;this.city=city;}// 省略}

客户端

publicclassOrgDemo{staticvoidmain(){// 给张三 西安AddressxianInfo=newAddress("朱雀大街","西安");Invitationinvitation=newInvitation("张三","诚挚邀请你参加年度庆典",xianInfo);// 给李四Invitationinvitation2=newInvitation("李四",invitation.getMessage(),invitation.getAddress());// 给王五Invitationinvitation3=newInvitation("王四",invitation.getMessage(),invitation.getAddress());}}

可以看到,需要重新new对象,并且需要知道细节来get set属性

4.2 原型模式

publicclassInvitationimplementsCloneable{StringpersonName;Stringmessage;Addressaddress;publicInvitation(StringpersonName,Stringmessage){this.personName=personName;this.message=message;}publicInvitation(StringpersonName,Stringmessage,Addressaddress){this.personName=personName;this.message=message;this.address=address;}@OverridepublicInvitationclone(){Invitationinvitation=null;try{invitation=(Invitation)super.clone();returninvitation;}catch(CloneNotSupportedExceptione){thrownewAssertionError();}}}publicclassAddress{Stringstreet;Stringcity;publicAddress(Stringstreet,Stringcity){this.street=street;this.city=city;}// 省略}

客户端

publicclassClient{staticvoidmain(){// 给张三 西安AddressxianInfo=newAddress("朱雀大街","西安");Invitationinvitation=newInvitation("张三","诚挚邀请你参加年度庆典",xianInfo);Invitationinvitation1=invitation.clone();invitation1.setPersonName("李四");Invitationinvitation2=invitation.clone();invitation2.setPersonName("王五");System.out.println(STR."\{invitation.getPersonName()}:\{invitation.getMessage()} 地点: \{invitation.getAddress().getCity()}");System.out.println(STR."\{invitation1.getPersonName()}:\{invitation1.getMessage()} 地点: \{invitation1.getAddress().getCity()}");System.out.println(STR."\{invitation2.getPersonName()}:\{invitation2.getMessage()} 地点: \{invitation2.getAddress().getCity()}");}}

不需要知道细节

5. 浅拷贝和深拷贝

5.1 概念

拷贝过程中会涉及对象属性如何拷贝问题,即是拷贝一个新对象还是只是拷贝地址(共享引用对象)
首先是java数据类型: 基本类型(8种基本类型及其包装类)、引用类型(字符串、数组、集合、其它)
对于基本类型,都是拷贝一个新值,即不影响原数据,对于引用类型:

  • 浅拷贝: 只拷贝地址(共享引用对象)。默认的clone()方法就是这个逻辑
  • 深拷贝: 拷贝新对象。也就是说对象修改不会影响原型。

5.2 浅拷贝示例

拷贝出来的实例,我们修改应用类型的属性进行观察

publicclassInvitationimplementsCloneable{StringpersonName;Stringmessage;Addressaddress;publicInvitation(StringpersonName,Stringmessage){this.personName=personName;this.message=message;}publicInvitation(StringpersonName,Stringmessage,Addressaddress){this.personName=personName;this.message=message;this.address=address;}@OverridepublicInvitationclone(){Invitationinvitation=null;try{invitation=(Invitation)super.clone();returninvitation;}catch(CloneNotSupportedExceptione){thrownewAssertionError();}}}publicclassAddress{Stringstreet;Stringcity;publicAddress(Stringstreet,Stringcity){this.street=street;this.city=city;}// 省略}
publicclassClient2{staticvoidmain(){// 给张三 西安AddressxianInfo=newAddress("朱雀大街","西安");Invitationinvitation=newInvitation("张三","诚挚邀请你参加年度庆典",xianInfo);Invitationinvitation1=invitation.clone();invitation1.setPersonName("李四");Invitationinvitation2=invitation.clone();invitation2.setPersonName("王五");invitation2.getAddress().setCity("上海");System.out.println(STR."\{invitation.getPersonName()}:\{invitation.getMessage()} 地点: \{invitation.getAddress().getCity()}");System.out.println(STR."\{invitation1.getPersonName()}:\{invitation1.getMessage()} 地点: \{invitation1.getAddress().getCity()}");System.out.println(STR."\{invitation2.getPersonName()}:\{invitation2.getMessage()} 地点: \{invitation2.getAddress().getCity()}");}}

输出:

张三:诚挚邀请你参加年度庆典 地点:上海 李四:诚挚邀请你参加年度庆典 地点:上海 王五:诚挚邀请你参加年度庆典 地点:上海

开始的地址是西安,在第二个实例中修改的地址为上海,结果输出可以看出,将所有地址都成了上海,也就是说原型和实例应用类型属性共享地址

5.3 深拷贝实现1-重新clone方法,自己控制属性深拷贝(不推荐)

publicclassInvitationimplementsCloneable{StringpersonName;Stringmessage;Addressaddress;publicInvitation(StringpersonName,Stringmessage){this.personName=personName;this.message=message;}publicInvitation(StringpersonName,Stringmessage,Addressaddress){this.personName=personName;this.message=message;this.address=address;}@OverridepublicInvitationclone(){Invitationinvitation=null;try{invitation=(Invitation)super.clone();// 这里新建对象invitation.setAddress(newAddress(invitation.getAddress().getStreet(),invitation.getAddress().getCity()));returninvitation;}catch(CloneNotSupportedExceptione){thrownewAssertionError();}}}publicclassAddress{Stringstreet;Stringcity;publicAddress(Stringstreet,Stringcity){this.street=street;this.city=city;}// 省略}

客户端:

publicclassClient2{staticvoidmain(){// 给张三 西安AddressxianInfo=newAddress("朱雀大街","西安");Invitationinvitation=newInvitation("张三","诚挚邀请你参加年度庆典",xianInfo);Invitationinvitation1=invitation.clone();invitation1.setPersonName("李四");Invitationinvitation2=invitation.clone();invitation2.setPersonName("王五");invitation2.getAddress().setCity("上海");System.out.println(STR."\{invitation.getPersonName()}:\{invitation.getMessage()} 地点: \{invitation.getAddress().getCity()}");System.out.println(STR."\{invitation1.getPersonName()}:\{invitation1.getMessage()} 地点: \{invitation1.getAddress().getCity()}");System.out.println(STR."\{invitation2.getPersonName()}:\{invitation2.getMessage()} 地点: \{invitation2.getAddress().getCity()}");}}

输出:

张三:诚挚邀请你参加年度庆典 地点:西安 李四:诚挚邀请你参加年度庆典 地点:西安 王五:诚挚邀请你参加年度庆典 地点:上海

可以看到实现了深拷贝。但是这种方式需要你知道代码实现细节,并且对于每个属性都需要自己手动去实现。

5.4 深拷贝实现2-序列化(推荐)

思路是把对象写到流里(序列化),再把流中对象读出来(反序列化)
工具:

publicclassCloneUtils{@SuppressWarnings("unchecked")publicstatic<TextendsSerializable>TdeepCopy(Tobject){if(object==null)returnnull;try(ByteArrayOutputStreambaos=newByteArrayOutputStream();ObjectOutputStreamoos=newObjectOutputStream(baos)){// 1. 序列化对象到字节数组oos.writeObject(object);oos.flush();try(ByteArrayInputStreambais=newByteArrayInputStream(baos.toByteArray());ObjectInputStreamois=newObjectInputStream(bais)){// 2. 反序列化创建新对象return(T)ois.readObject();}}catch(IOException|ClassNotFoundExceptione){thrownewRuntimeException("深拷贝失败",e);}}}

po实现序列化接口

publicclassInvitationimplementsSerializable{StringpersonName;Stringmessage;Addressaddress;publicInvitation(StringpersonName,Stringmessage){this.personName=personName;this.message=message;}publicInvitation(StringpersonName,Stringmessage,Addressaddress){this.personName=personName;this.message=message;this.address=address;}// 省略}publicclassAddressimplementsSerializable{Stringstreet;Stringcity;publicAddress(Stringstreet,Stringcity){this.street=street;this.city=city;}// 省略}

客户端:

publicclassClient2{staticvoidmain(){// 给张三 西安AddressxianInfo=newAddress("朱雀大街","西安");Invitationinvitation=newInvitation("张三","诚挚邀请你参加年度庆典",xianInfo);Invitationinvitation1=CloneUtils.deepCopy(invitation);invitation1.setPersonName("李四");Invitationinvitation2=CloneUtils.deepCopy(invitation);invitation2.setPersonName("王五");invitation2.getAddress().setCity("上海");System.out.println(STR."\{invitation.getPersonName()}:\{invitation.getMessage()} 地点: \{invitation.getAddress().getCity()}");System.out.println(STR."\{invitation1.getPersonName()}:\{invitation1.getMessage()} 地点: \{invitation1.getAddress().getCity()}");System.out.println(STR."\{invitation2.getPersonName()}:\{invitation2.getMessage()} 地点: \{invitation2.getAddress().getCity()}");}}

输出;

张三:诚挚邀请你参加年度庆典 地点:西安 李四:诚挚邀请你参加年度庆典 地点:西安 王五:诚挚邀请你参加年度庆典 地点:上海

6. 原型模式优缺点

结核1核(高内聚低耦合)4性(复用性 可读性 维护性 稳定性)7大原则(设计原则)

  • 优点:
    1. 高内聚低耦合: 创建逻辑和使用分离,降低耦合度
    2. 复用性: 高效复用已对象,避免重复初始化
    3. 开闭原则: 可以动态添加具体原型,无序修改现有代码
    4. 接口隔离: Cloneable接口最小化
    5. 迪米特法则: 封装克隆的实现,只需要调用clone()方法
  • 缺点:
    1. 维护性: 深拷贝实现复杂,维护困难

参考:

  • 韩顺平 Java设计模式
  • 张维鹏 Java设计模式之创建型:原型模式)
  • kosamino 设计模式之原型模式(Prototype)详解及代码示例

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询