一文带你了解Java创建型设计模式之原型模式

 

原型模式

概述

原型模式(Prototype Pattern)是属于创建型模式。

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

原型模式的核心在于拷贝原型对象。以存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时的对象初始化过程(不调用构造函数),性能提升许多。

当直接创建对象的代价比较大时,则采用这种模式。利用当前系统中已存在的对象作为原型,对其进行克隆,避免初始化的过程。

优缺点

优点:

1.java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升许多

2.使用原型模式将对象复制一份并将其状态保存起来,简化创建对象的过程,以便在需要的时候使用

缺点:

1.需要为每一个类配置一个克隆方法

2克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反开闭原测

3.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支特深克隆,实现比较麻烦

应用场景

1.类初始化消耗资源较多

2.new产生的一个对象需要非常繁琐的过程(数据准备或访问权限)

3.构造函数处比较复杂

4.循环体中生产大量对象时

5.资源优化场景

6.性能和安全要求的场景

主要角色

1.客户(Client)角色

客户端类向原型管理器提出创建对象的请求。

2.抽象原型(Prototype)角色

这是一个抽象角色,此角色给出所有的具体原型类所需的接口。

3.具体原型(Concrete Prototype)角色

被复制的对象。此角色需要实现抽象的原型角色所要求的接口。

4.原型管理器(Prototype Manager)角色

创建具体原型类的对象,并记录每一个被创建的对象。

 

原型模式的基本使用

创建原型接口

public interface IPrototype<T> {
  T clone();
}

创建具体需要克隆对象

@Data
@ToString
public class ConcretePrototype implements IPrototype {
  private int age;
  private String name;
  @Override
  public ConcretePrototype clone() {
      ConcretePrototype concretePrototype = new ConcretePrototype();
      concretePrototype.setAge(this.age);
      concretePrototype.setName(this.name);
      return concretePrototype;
  }
}

使用

    public static void main(String[] args) {
      //创建原型对象
      ConcretePrototype prototype = new ConcretePrototype();
      prototype.setAge(20);
      prototype.setName("jack");
      System.out.println(prototype);
      //拷贝原型对象
      ConcretePrototype cloneType = prototype.clone();
      System.out.println(cloneType);
  }

 

JDK自带原型接口的使用

原型模式就是如此简单,通常开发中不会这样使用。其实JDK提供了一个现成的API接口,那就是Cloneable接口。

@Data
@ToString
public class ConcretePrototype implements Cloneable {
  private int age;
  private String name;
  private List<String> hobbies;
  @Override
  public ConcretePrototype clone() {
      try {
          return (ConcretePrototype)super.clone();
      } catch (CloneNotSupportedException e) {
          e.printStackTrace();
          return null;
      }
  }
}

 

浅克隆与深度克隆

浅克隆

添加一个hobby属性,当给克隆对象的hobby属性添加一项时,最终结果会导致原型对象发生变化,也就是hobby属性用于一个内存地址。这就是浅克隆。

@Data
@ToString
public class ConcretePrototype implements Cloneable {
  private int age;
  private String name;
  private List<String> hobby;
  @Override
  public ConcretePrototype clone() {
      try {
          return (ConcretePrototype)super.clone();
      } catch (CloneNotSupportedException e) {
          e.printStackTrace();
          return null;
      }
  }
}
    public static void main(String[] args) {
      ConcretePrototype prototype = new ConcretePrototype();
      prototype.setAge(22);
      prototype.setName("jack");
      List<String> hobby = new ArrayList<String>();
      hobby.add("java");
      hobby.add("python");
      hobby.add("go");
      prototype.setHobby(hobby);
      // 拷贝原型对象
      ConcretePrototype cloneType = prototype.clone();
      cloneType.getHobby().add("php");
      System.out.println("原型对象:" + prototype);
      System.out.println("克隆对象:" + cloneType);
      System.out.println(prototype == cloneType);
      System.out.println(prototype.getHobby() == cloneType.getHobby());
  }

原型对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
克隆对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
true

深度克隆

可使用序列化方式实现对象的深度克隆

@Data
@ToString
public class ConcretePrototype implements Cloneable, Serializable {
  private int age;
  private String name;
  private List<String> hobby;
  @Override
  public ConcretePrototype clone() {
      try {
          return (ConcretePrototype) super.clone();
      } catch (CloneNotSupportedException e) {
          e.printStackTrace();
          return null;
      }
  }
  public ConcretePrototype deepClone() {
      try {
          ByteArrayOutputStream bos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(bos);
          oos.writeObject(this);
          ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bis);
          return (ConcretePrototype) ois.readObject();
      } catch (Exception e) {
          e.printStackTrace();
          return null;
      }
  }
}
    public static void main(String[] args) {
      //创建原型对象
      ConcretePrototype prototype = new ConcretePrototype();
      prototype.setAge(22);
      prototype.setName("jack");
      List<String> hobby = new ArrayList<String>();
      hobby.add("java");
      hobby.add("python");
      hobby.add("go");
      prototype.setHobby(hobby);
      // 拷贝原型对象
      ConcretePrototype cloneType = prototype.deepClone();
      cloneType.getHobby().add("php");
      System.out.println("原型对象:" + prototype);
      System.out.println("克隆对象:" + cloneType);
      System.out.println(prototype == cloneType);
      System.out.println(prototype.getHobby() == cloneType.getHobby());
  }

原型对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go])
克隆对象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
false

也可这样操作,多克隆一次

    public ConcretePrototype deepClone2() {
      try {
          ConcretePrototype result = (ConcretePrototype) super.clone();
          result.hobby = (List) ((ArrayList) result.hobby).clone();
          return result;
      } catch (CloneNotSupportedException e) {
          e.printStackTrace();
          return null;
      }
  }

 

单例的破坏

如果克隆的目标对象是单例对象,深克隆就会破坏单例。

解决方案:禁止深克隆。

1.让单例类不实现Cloneable接口

2.重写clone方法,在clone方法中返回单例对象即可

@Data
@ToString
public class ConcretePrototype implements Cloneable {
  private static  ConcretePrototype instance = new ConcretePrototype();
  private ConcretePrototype(){}
  public static ConcretePrototype getInstance(){
      return instance;
  }
  @Override
  public ConcretePrototype clone() {
     return instance;
  }
}

关于一文带你了解Java创建型设计模式之原型模式的文章就介绍至此,更多相关Java原型模式内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 - 分布式锁需要具备的条件和刚需独占性:OnlyOne,任何时刻只能有且仅有一个线程持有高可用:若redis集群环境下,不能因为某一个节点挂了而出 ...