设计模式--原型模式

  1. 原型模式中利用一个原型对象来指明我们需要创建对象的类型,然后通过赋值这个对象来获取一个与该对象一模一样的对象实例。

  2. 原型模式就是用原型实例指定创建对象的种类,并且通过克隆这些原型来创建新的对象。

  3. 在原型模式中,创建对象是通过请求原型对象类型来拷贝原型对象自己来创建对象,也就是只需要知道原型对象的类型就可以获得更多的原型实例对象。

  4. 拷贝的类型

    1. 浅拷贝:使用一个已知实例对新创建的实例的成员变量逐个赋值,其实是对值得地址引用。
    2. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形参实例值(真的复制了成员变量)。
  5. 原型模式的UML图

  6. 原型模式的代码实现

    1. 创建一个需要克隆的对象,需要实现Serializable接口

      import java.io.*;
      import java.util.Date;
      import java.util.List;
      
      public class Student implements Serializable {
      
          private String name;
          private Date birthDay;
      
          private School school;
          private List<String> friends;
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Date getBirthDay() {
              return birthDay;
          }
      
          public void setBirthDay(Date birthDay) {
              this.birthDay = birthDay;
          }
      
          public School getSchool() {
              return school;
          }
      
          public void setSchool(School school) {
              this.school = school;
          }
      
          public List<String> getFriends() {
              return friends;
          }
      
          public void setFriends(List<String> friends) {
              this.friends = friends;
          }
      
          /**
           * 核心方法,用于克隆Student对象
           * @return
           * @throws CloneNotSupportedException
           */
          public Student deepClone() throws IOException, ClassNotFoundException {
              //第一版,浅克隆,需要实现Cloneable接口
              //Student student = (Student)super.clone();
      
              //第二版,普通克隆,需要实现Cloneable接口,但是无法克隆JDK类中没有实现Cloneable接口的类
              /*Student student = (Student)super.clone();
              student.school = (School) school.clone();
              */
      
              //第三版,深度克隆,不需要实现Cloneable接口,关联的对象需要实现Serializable接口,
              //  深度克隆是基于序列化接口的,序列化相关的类,是对流的复制操作,与Cloneable无关
              //将对象写入到流里
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(baos);
              oos.writeObject(this);
      
              //从流里将对象读出
              ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
              ObjectInputStream ois = new ObjectInputStream(bais);
              Student student = (Student)ois.readObject();
      
              return student;
          }
      }
      
    2. 创建一个克隆对象引用的对象,需要实现Serializable接口

      import java.io.Serializable;
      
      public class School implements Serializable {
      
          private String name;
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
    3. 编写克隆的测试类

      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.Date;
      import java.util.List;
      
      public class Test {
          public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
              Student student1 = new Student();
              student1.setName("张三");
              student1.setBirthDay(new Date());
              List<String> friends = new ArrayList<>();
              friends.add("zhangsan");
              friends.add("lisi");
              student1.setFriends(friends);
              School school = new School();
              school.setName("剑桥");
              student1.setSchool(school);
      
      
              Student student2 = student1.deepClone();
              student2.setName("李四");
              student2.getSchool().setName("清华");
              student2.getFriends().add("王五");
      
              System.out.println("student1.name: "+student1.getName());
              System.out.println("student2.name: "+student2.getName());
      
              System.out.println("student1.birthday: "+student1.getBirthDay());
              System.out.println("student2.birthday: "+student2.getBirthDay());
      
              System.out.println("student1.friends: "+student1.getFriends()+student1.getFriends().size());
              System.out.println("student2.friends: "+student2.getFriends()+student1.getFriends().size());
      
              System.out.println("student1.school: "+student1.getSchool().getName());
              System.out.println("student2.school: "+student2.getSchool().getName());
      
          }
      }
      /**
          执行结果为:
              student1.name: 张三
              student2.name: 李四
              student1.birthday: Sun Dec 08 15:45:29 CST 2019
              student2.birthday: Sun Dec 08 15:45:29 CST 2019
              student1.friends: [zhangsan, lisi]2
              student2.friends: [zhangsan, lisi, 王五]2
              student1.school: 剑桥
              student2.school: 清华
      */
      
  7. 原型模式的优点

    1. 如果创建新的对象比较复杂时,可以利用原型模式简化对对象的创建过程,同时也能够提高效率。
    2. 可以使用深度克隆保持对象的状态
    3. 原型模式提供了简化的创建结构
  8. 原型模式的缺点

    1. 在实现深度克隆的时候可能需要比较复杂的代码
    2. 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,必须要修改其源代码,违背了"开闭原则"
  9. 使用场景

    1. 创建新对象成本较高,可以利用已有的对象进行克隆
    2. 如果系统要保持对象状态,且对象变化小,或者对象本身占用内存不大时,可以使用原型模式或者备忘录模式来使用;相反,如果对象的变化较大或者占用的内存很大,建议使用原型模式
    3. 需要避免使用分层次的工厂类来创建分层次的对象(一个对象中引用了别的对象),并且类的实例对象只有一个或者很少的几个组合状态,通过复制原型对象得到新实例比构造函数创建一个新实例更加方便
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值