前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式(十一):原型模式

设计模式(十一):原型模式

作者头像
xujjj
发布2019-07-31 11:43:47
3700
发布2019-07-31 11:43:47
举报
文章被收录于专栏:IT界的泥石流IT界的泥石流

什么是原型模式

定义:用原型实例指定创建对象的种类,并且通过复制原型实例创建新的对象。

我们都看过《西游记》,里面孙悟空拥有拔毛变分身的技能,只需要拔毛轻轻一吹就会变出许许多多的孙悟空。孙悟空本体就是原型实例,通过分身术复制变出新的孙悟空,分身孙悟空与本体孙悟空相对独立,即分身孙悟空遇到任何问题,都不会影响本体孙悟空。原型模式正是采用这种思想,用一个已经创建成功的对象实例作为原型,通过复制该原型实例来创建一个和原型实例相同或相似的新对象。

原型模式的应用场景

  • 类初始化需要消耗非常多数据计算、硬件资源等资源时
  • 通过 new 关键词创建的一个对象需要非常繁琐的数据准备或者权限校验时
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时
  • 对对象本身的克隆需要涉及到类本身的数据细节,需要隐藏克隆操作的细节时
  • 希望对目标对象的修改不影响原有的原型对象时
  • 希望创建一个对象就可以得到已有原型对象的数据,而不是创建一个纯洁的对象时(如创建孙悟空分身,也希望分身孙悟空也会施放技能,而不是看起来像孙悟空,什么技能都不会)

原型模式的优缺点

原型模式的优点如下:

  • 当创建新对象实例比较繁琐时,使用原型模式复制一个已有实例可以提高新对象的创建效率
  • 可以避免构造函数的约束(复制过程不会执行构造函数)
  • 可以使用深克隆的方式保存对象的状态

原型模式的缺点如下:

  • 每一个类都必须配备一个克隆方法。这对于全新的类来说不是很难,而对于已经有的类不一定很容易,当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的
  • 必须实现 Cloneable 接口
  • 由于原型模式不会执行构造函数,可能存在问题

原型模式的结构与实现

1、结构

原型模式的角色如下。

1.用户角色(Client):使用者调用具体原型对象中的 clone 方法完成对象的复制创建。

2.抽象原型角色(Prototype):继承了 Cloneable 接口,声明具备clone能力。

3.具体原型角色(ConcretePrototype):实现了 clone 方法。

2、实现

浅克隆的实现

代码语言:javascript
复制
public class ShallowCopyDemo implements Cloneable {
  private String demoName;
  private int demoInt;
  
  private Helper helper = new Helper();
  private List<String> demoList = new ArrayList<>();
  public String getDemoName() {
    return demoName;
  }
  public void setDemoName(String demoName) {
    this.demoName = demoName;
  }
  public int getDemoInt() {
    return demoInt;
  }
  public void setDemoInt(int demoInt) {
    this.demoInt = demoInt;
  }
  public String getHelperName() {
    return helper.getHelperName();
  }
  public void setHelperName(String helperName) {
    this.helper.setHelperName(helperName);
  }
  public void addToDemoList(String demoName) {
    demoList.add(demoName);
  }
  public void showDemoList() {
    for(String s : this.demoList) {
      System.out.println(s);
    }
  }
  
  @Override
  public String toString() {
    return "ShallowCopyDemo [demoName=" + demoName + ", demoInt=" + demoInt + ", helperName=" + helper.getHelperName() + " ]";
  }
  
  @Override
  protected ShallowCopyDemo clone() throws CloneNotSupportedException {
    ShallowCopyDemo shallowCopyDemo = null;
    try {
      shallowCopyDemo = (ShallowCopyDemo) super.clone();
    } catch (Exception e) {
      return null;
    }
    return shallowCopyDemo;
  }
}
代码语言:javascript
复制
public class Helper {
  private String helperName;
  public String getHelperName() {
    return helperName;
  }

  public void setHelperName(String helperName) {
    this.helperName = helperName;
  }
}
代码语言:javascript
复制
public class ClientTest {
  public static void main(String[] args) throws CloneNotSupportedException {
    ShallowCopyDemo shallowCopyDemo = new ShallowCopyDemo();
    shallowCopyDemo.setDemoInt(10);
    shallowCopyDemo.setDemoName("IT界的泥石流");
    shallowCopyDemo.addToDemoList("请关注公众号:IT界的泥石流");
    shallowCopyDemo.setHelperName("非常感谢");
    shallowCopyDemo.showDemoList();
    System.out.println(shallowCopyDemo.toString());
    
    ShallowCopyDemo shallowCopyDemo2 = shallowCopyDemo.clone();
    shallowCopyDemo.setDemoInt(100);
    shallowCopyDemo.setDemoName("祝工作顺利");
    shallowCopyDemo2.addToDemoList("学习进步");
    shallowCopyDemo2.setHelperName("谢谢支持");
    
    shallowCopyDemo.showDemoList();
    System.out.println(shallowCopyDemo.toString());
    
    shallowCopyDemo2.showDemoList();
    System.out.println(shallowCopyDemo2.toString());
  }
}
代码语言:javascript
复制
执行结果:
请关注公众号:IT界的泥石流
ShallowCopyDemo [demoName=IT界的泥石流, demoInt=10, helperName=非常感谢 ]
请关注公众号:IT界的泥石流
学习进步
ShallowCopyDemo [demoName=祝工作顺利, demoInt=100, helperName=谢谢支持 ]
请关注公众号:IT界的泥石流
学习进步
ShallowCopyDemo [demoName=IT界的泥石流, demoInt=10, helperName=谢谢支持 ]

我们首先创建一个 ShallowCopyDemo 实例 shallowCopyDemo 对其赋值并输出结果,输出结果符合预期。接着我们使用 clone 方法复制出另一个实例 shallowCopyDemo2 对其赋值后,再次输出 shallowCopyDemo 和 shallowCopyDemo2 的结果,从输出结果看出第一个实例 shallowCopyDemo 的 demoList 和 helper 变量的值被修改了。

不是说好的复制出全新的一份吗?应该是相互独立的才对。这是因为 clone 方法在java中除了基本数据类型和String类型外,数组、集合引用和对象引用的成员变量都不会被拷贝,它们是多个克隆实例与本体共用那一部分数据的,这是浅克隆。

深克隆的实现

当我们希望克隆实例与本体相互独立时,应使用深克隆。

代码语言:javascript
复制
public class DeepCopyDemo implements Serializable {
  private String demoName;
  private int demoInt;
  
  private Helper helper = new Helper();
  private List<String> demoList = new ArrayList<>();
  public String getDemoName() {
    return demoName;
  }
  public void setDemoName(String demoName) {
    this.demoName = demoName;
  }
  public int getDemoInt() {
    return demoInt;
  }
  public void setDemoInt(int demoInt) {
    this.demoInt = demoInt;
  }
  public String getHelperName() {
    return helper.getHelperName();
  }
  public void setHelperName(String helperName) {
    this.helper.setHelperName(helperName);
  }
  public void addToDemoList(String demoName) {
    demoList.add(demoName);
  }
  public void showDemoList() {
    for(String s : this.demoList) {
      System.out.println(s);
    }
  }
  
  @Override
  public String toString() {
    return "ShallowCopyDemo [demoName=" + demoName + ", demoInt=" + demoInt + ", helperName=" + helper.getHelperName() + " ]";
  }
  
  public DeepCopyDemo deepClone() throws IOException, ClassNotFoundException {
        //将对象写到流里
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //从流里读回来
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (DeepCopyDemo) ois.readObject();
    }
}
代码语言:javascript
复制
public class Helper implements Serializable {
  private String helperName;
  public String getHelperName() {
    return helperName;
  }

  public void setHelperName(String helperName) {
    this.helperName = helperName;
  }
}
代码语言:javascript
复制
public class ClientTest {
  public static void main(String[] args) throws ClassNotFoundException, IOException {
    DeepCopyDemo deepCopyDemo = new DeepCopyDemo();
    deepCopyDemo.setDemoInt(10);
    deepCopyDemo.setDemoName("IT界的泥石流");
    deepCopyDemo.addToDemoList("请关注公众号:IT界的泥石流");
    deepCopyDemo.setHelperName("非常感谢");
    deepCopyDemo.showDemoList();
    System.out.println(deepCopyDemo.toString());
    
    DeepCopyDemo deepCopyDemo2 = deepCopyDemo.deepClone();
    deepCopyDemo2.setDemoInt(100);
    deepCopyDemo2.setDemoName("祝工作顺利");
    deepCopyDemo2.addToDemoList("学习进步");
    deepCopyDemo2.setHelperName("谢谢支持");
    
    deepCopyDemo.showDemoList();
    System.out.println(deepCopyDemo.toString());
    
    deepCopyDemo2.showDemoList();
    System.out.println(deepCopyDemo2.toString());
  }
}
代码语言:javascript
复制
执行结果:
请关注公众号:IT界的泥石流
ShallowCopyDemo [demoName=IT界的泥石流, demoInt=10, helperName=非常感谢 ]
请关注公众号:IT界的泥石流
ShallowCopyDemo [demoName=IT界的泥石流, demoInt=10, helperName=非常感谢 ]
请关注公众号:IT界的泥石流
学习进步
ShallowCopyDemo [demoName=祝工作顺利, demoInt=100, helperName=谢谢支持 ]

DeepCopyDemo 与 ShallowCopyDemo 差别在于 DeepCopyDemo 不实现 Cloneable 接口,而是实现 Serializable,并通过序列化与反序列化的方式完成对象复制。

我们从运行结果上可以看到克隆实例和本体互不影响。

以上就是今天《原型模式》的讲解,至此创建型模式已讲述完毕。良好的代码风格需要长期不断的积累学习。各位读者大人若有问题,欢迎后台留言,我将第一时间回复!

下期文章将介绍《设计模式(十二):代理模式》

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT界的泥石流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档