在开发一个界面的时候,里面有多个Button,这些对象的属性内容相似。如果一个个实例化Button对象,并设置其属性,那么代码量将会增多。
通过一个原型对象克隆出多个一模一样的对象,该模式被称为原型模式。
图 原型模式
Prototype: 抽象原型类,是声名克隆方法的接口,也可以是具体原型类的公共父类。
ConcretePrototype:具体原型类,实现了克隆方法,在克隆方法中返回自己的一个克隆对象。
Client:客户类,让一个原型对象克隆自身从而创建一个新的对象。
public interface ButtonClone {ButtonClone cloneBtn();}public class Button implements ButtonClone{private Double width;private Double height;public Double getWidth() {return width;}public void setWidth(Double width) {this.width = width;}public Double getHeight() {return height;}public void setHeight(Double height) {this.height = height;}@Overridepublic ButtonClone cloneBtn() {Button newBtn = new Button();newBtn.setHeight(this.height);newBtn.setWidth(this.width);return newBtn;}
}
1 浅克隆与深克隆
浅克隆,如果源对象的成员变量是值类型,将复制一份给克隆对象,如果源对象的成员变量是引用类型,则将引用对象的地址复制一份给对象。
深克隆,无论源对象的成员变量是值类型还是引用类型,都将复制一份给目标对象。
1.1 浅克隆
Java的Object类提供了一个clone()方法,可以将一个Java对象浅克隆。能实现克隆Java类必须实现一个标识接口Cloneable,表示这个类支持被复制。
public class ConcretePrototype implements Cloneable{private String name;private String type;private List<String> list = new ArrayList<>();public ConcretePrototype(String name, String type) {this.name = name;this.type = type;}@Overridepublic String toString() {return "ConcretePrototype{" +"name='" + name + '\'' +", type='" + type + '\'' +", list=" + list +'}';}public static void main(String[] args) throws CloneNotSupportedException {ConcretePrototype prototype = new ConcretePrototype("实际类型", "浅克隆");System.out.println("prototype:" + prototype);Object clone = prototype.clone();System.out.println("clone:" + clone);prototype.list.add("hello");System.out.println("clone:" + clone);}}
1.2 深克隆
深克隆,除了对象本身被复制外,对象所包含的所有成员变量也将被复制。在Java中,如果需要实现深克隆,可以通过序列化等方式来实现。需要实例化的对象其类必须实现Serializable接口,否则无法实现序列化操作。
public class CusForm implements Serializable , Cloneable{private String name;private CusFile cusFile;public CusForm(String name, CusFile cusFile) {this.name = name;this.cusFile = cusFile;}public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {CusForm cusForm = new CusForm("表单", new CusFile());System.out.println(cusForm);System.out.println(cusForm.clone()); //java浅克隆,对象成员变量直接复制地址CusForm deepCloneObj = cusForm.deepClone();System.out.println(deepCloneObj);// 运行结果
// CusForm{name='表单', cusFile=com.huangmingfu.prototype.deep.CusForm$CusFile@1b6d3586}
// CusForm{name='表单', cusFile=com.huangmingfu.prototype.deep.CusForm$CusFile@1ddc4ec2}
// CusForm{name='表单', cusFile=com.huangmingfu.prototype.deep.CusForm$CusFile@1b6d3586}}public CusForm deepClone() throws IOException, ClassNotFoundException {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeObject(this);ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);return (CusForm) objectInputStream.readObject();}@Overridepublic String toString() {return "CusForm{" +"name='" + name + '\'' +", cusFile=" + cusFile +'}';}private static class CusFile implements Serializable{}
}
2 原型管理器
将多个原型对象存储在一个集合中供客户端使用,它是一个专门复制克隆对象的工厂。其中定义了一个集合用于存储原型对象。
图 原型管理器
public class DeepClone implements Serializable {public DeepClone deepClone() {try {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeObject(this);ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);return (DeepClone)objectInputStream.readObject();} catch (Exception e) {System.out.println(e.getMessage());}return null;}
}public class ConcreteA extends DeepClone{private final String name = "concreteA";@Overridepublic String toString() {return "ConcreteA{" +"name='" + name + '\'' +'}' + super.toString();}}public class ConcreteB extends DeepClone{private final String name = "ConcreteB";@Overridepublic String toString() {return "ConcreteB{" +"name='" + name + '\'' +'}' + super.toString();}
}public class PrototypeManager {private final Map<Class<? extends DeepClone>,DeepClone> prototypeMap = new HashMap<>();private final Set<Class<? extends DeepClone>> classes = new HashSet<>();private PrototypeManager() {}private static class HolderClass {private final static PrototypeManager instance = new PrototypeManager();}public static PrototypeManager getInstance() {return HolderClass.instance;}public void addPrototype(DeepClone deepClone) {if (classes.add(deepClone.getClass())) {prototypeMap.put(deepClone.getClass(),deepClone);}}public DeepClone getClone(Class<? extends DeepClone> cls) {DeepClone deepClone = prototypeMap.get(cls);if (deepClone == null) {throw new RuntimeException("克隆体不存在");}return deepClone.deepClone();}}public class Client {public static void main(String[] args) {ConcreteA concreteA = new ConcreteA();ConcreteB concreteB = new ConcreteB();PrototypeManager prototypeManager = PrototypeManager.getInstance();System.out.println(concreteA);System.out.println(concreteB);prototypeManager.addPrototype(concreteA);prototypeManager.addPrototype(concreteB);System.out.println("克隆分割线---------------");DeepClone deepCloneA = prototypeManager.getClone(ConcreteA.class);DeepClone deepCloneB = prototypeManager.getClone(ConcreteB.class);System.out.println(deepCloneA);System.out.println(deepCloneB);// 运行结果
// ConcreteA{name='concreteA'}com.huangmingfu.prototype.manager.ConcreteA@1b6d3586
// ConcreteB{name='ConcreteB'}com.huangmingfu.prototype.manager.ConcreteB@4554617c
// 克隆分割线---------------
// ConcreteA{name='concreteA'}com.huangmingfu.prototype.manager.ConcreteA@30dae81
// ConcreteB{name='ConcreteB'}com.huangmingfu.prototype.manager.ConcreteB@1b2c6ec2}}
3 适用场景
当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建工厂,通过复制一个已有实例可以提高新实例的创建效率。
- 创建新对象成本较大。
- 对象的状态变化很小,且系统需要保存对象的状态时。
- 需要创建许多相似对象。