• 原型模式(创建型)


    一、前言

            原型模式是一种创建型设计模式,它允许在运行时通过克隆现有对象来创建新对象,而不是通过常规的构造函数创建。在原型模式中,一个原型对象可以克隆自身来创建新的对象,这个过程可以通过深度克隆或浅克隆来实现。简单说原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

            原型模式包含三个角色:

            ①Prototype(抽象原型类):定义用于克隆自身的接口,通常是一个抽象类或接口,其中声明了一个克隆方法 clone(),用于创建一个新的对象,具体的克隆操作由子类实现。

            ②ConcretePrototype(具体原型类):实现 Prototype 接口,实现 clone() 方法,完成对象的克隆操作。每个具体原型类都有自己的一些属性和方法,不同的具体原型类之间具有不同的实现方式。

            ③Client(客户类):使用原型模式创建新的对象,在原型模式中,客户端通过克隆接口来创建新的对象,而不是通过实例化的方式。客户端需要获取一个原型对象,并通过克隆方法来创建新的对象,从而避免了创建新对象的开销。

            原型模式的克隆分为浅拷贝和深拷贝两种。

    二、浅拷贝

            创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

            这里抽象原型类不用自己创建,都是Object,里面有clone()方法:

            具体原型类实现Cloneable,里面重写clone方法,直接super调用父类的clone方法即可:

    1. import java.util.List;
    2. /**
    3. * @Author dengyifan
    4. * @create 2023/11/10 10:19
    5. * @description
    6. */
    7. public class UserPrototype implements Cloneable{
    8. private String name;
    9. private Integer age;
    10. private List messages;
    11. public String getName() {
    12. return name;
    13. }
    14. public void setName(String name) {
    15. this.name = name;
    16. }
    17. public Integer getAge() {
    18. return age;
    19. }
    20. public void setAge(Integer age) {
    21. this.age = age;
    22. }
    23. public List getMessages() {
    24. return messages;
    25. }
    26. public void setMessages(List messages) {
    27. this.messages = messages;
    28. }
    29. @Override
    30. protected UserPrototype clone() throws CloneNotSupportedException {
    31. return (UserPrototype) super.clone();
    32. }
    33. }

            客户类:

    1. import java.util.ArrayList;
    2. import java.util.List;
    3. /**
    4. * @Author dengyifan
    5. * @create 2023/11/10 10:22
    6. * @description
    7. */
    8. public class PrototypeClient {
    9. public static void main(String[] args) throws CloneNotSupportedException {
    10. UserPrototype userPrototype1 = new UserPrototype();
    11. userPrototype1.setName("tom");
    12. userPrototype1.setAge(18);
    13. List messages1 = new ArrayList<>();
    14. messages1.add("test");
    15. userPrototype1.setMessages(messages1);
    16. UserPrototype userPrototype2 = userPrototype1.clone();
    17. System.err.println(String.format("两个对象是否一致:%s", userPrototype1 == userPrototype2));
    18. System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototype1.getName() == userPrototype2.getName()));
    19. System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototype1.getMessages() == userPrototype2.getMessages()));
    20. System.err.println(userPrototype1.getMessages());
    21. System.err.println(userPrototype2.getMessages());
    22. messages1.clear();
    23. messages1.add("test1");
    24. System.err.println(userPrototype1.getMessages());
    25. System.err.println(userPrototype2.getMessages());
    26. }
    27. }

            运行结果:

            通过结果可以看到,通过克隆的对象与原型对象的引用不一致,说明再内存中存在两个不同的对象,一个是原型对象,一个是克隆生成的对象。而这里属性都显示是一致的,结果都是true,说明两个对象的成员对象是同一个,也就是对象本身是复制了,但是其成员的对象再内存中没有复制。并且我们修改原型的messages属性,克隆对象的messages也跟着改变,说明属性确实是同一个对象的引用。

    三、深拷贝

            深拷贝中除了原型对象与克隆生成的对象被复制以外,里面的属性也需要被复制,即地址都不一样。这里深拷贝实现了Serializable,即进行了序列化。

    1. import java.io.*;
    2. import java.util.List;
    3. /**
    4. * @Author dengyifan
    5. * @create 2023/11/10 15:43
    6. * @description
    7. */
    8. public class UserPrototypeDeep implements Serializable {
    9. private String name;
    10. private Integer age;
    11. private List messages;
    12. public String getName() {
    13. return name;
    14. }
    15. public void setName(String name) {
    16. this.name = name;
    17. }
    18. public Integer getAge() {
    19. return age;
    20. }
    21. public void setAge(Integer age) {
    22. this.age = age;
    23. }
    24. public List getMessages() {
    25. return messages;
    26. }
    27. public void setMessages(List messages) {
    28. this.messages = messages;
    29. }
    30. protected UserPrototypeDeep deepClone() throws Exception{
    31. ByteArrayOutputStream bao=new ByteArrayOutputStream();
    32. ObjectOutputStream oos=new ObjectOutputStream(bao);
    33. oos.writeObject(this);
    34. //将对象从流中取出
    35. ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
    36. ObjectInputStream ois=new ObjectInputStream(bis);
    37. return (UserPrototypeDeep) ois.readObject();
    38. }
    39. }
    1. import java.util.ArrayList;
    2. import java.util.List;
    3. /**
    4. * @Author dengyifan
    5. * @create 2023/11/10 15:46
    6. * @description
    7. */
    8. public class PrototypeDeepClient {
    9. public static void main(String[] args) throws Exception {
    10. UserPrototypeDeep userPrototypeDeep1 = new UserPrototypeDeep();
    11. userPrototypeDeep1.setAge(19);
    12. userPrototypeDeep1.setName("tom");
    13. List messages1 = new ArrayList<>();
    14. messages1.add("test");
    15. userPrototypeDeep1.setMessages(messages1);
    16. UserPrototypeDeep userPrototypeDeep2 = userPrototypeDeep1.deepClone();
    17. System.err.println(String.format("两个对象是否一致:%s", userPrototypeDeep1 == userPrototypeDeep2));
    18. System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototypeDeep1.getName() == userPrototypeDeep2.getName()));
    19. System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototypeDeep1.getMessages() == userPrototypeDeep2.getMessages()));
    20. System.err.println(userPrototypeDeep1.getMessages());
    21. System.err.println(userPrototypeDeep2.getMessages());
    22. messages1.clear();
    23. messages1.add("test1");
    24. System.err.println(userPrototypeDeep1.getMessages());
    25. System.err.println(userPrototypeDeep2.getMessages());
    26. }
    27. }

            运行结果:

            可以看出除了对象本身不一致以外,里面的属性也不一致,当修改原型对象里面引用对象的属性时,克隆对象的属性不会发生变化,即也证明属性不是同一个对象。

            对于原型模式的应用场景,主要有以下几点:

    1. 当对象的创建过程非常复杂或耗费大量资源时,可以使用原型模式来复制一个现有对象,从而避免重复创建对象。

    2. 当需要创建多个相似的对象时,可以使用原型模式来减少重复代码,提高代码的重用性。

    3. 当创建对象的过程需要大量的数据准备或配置时,可以使用原型模式来复制一个已经配置好的对象,从而避免重复的数据准备或配置过程。

    4. 当需要动态生成对象的子类时,可以使用原型模式来复制一个已有的对象,并对其进行修改,从而避免重新编写子类的代码。

    5. 当需要保护对象的状态时,可以使用原型模式来创建对象的副本,并将副本交给其他对象使用,从而避免原始对象的状态被修改。

  • 相关阅读:
    一键编译+执行c语言小Demo
    Elastic Stack从入门到实践(一)--Elastic Stack入门(1)--Elasticsearch与Kibana入门
    Spring 6【p命名空间和c命名空间】(五)-全面详解(学习总结---从入门到深化)
    一整套美团面经(给对象超用心整理的)
    cv2.dnn.readNetFromONNX读取yolov8的onnx报错解决过程
    iptables学习
    LQ0272 矩形运算【计算几何】
    一次I/O操作的过程
    国标视频融合云平台EasyCVR视频汇聚平台关于远程控制的详细介绍
    Spring MVC
  • 原文地址:https://blog.csdn.net/qq_41061437/article/details/115031828