原型模式是创建型模式的一种,其特点在于通过 “复制” 一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的 “原型”,这个原型是可定制的。
类图
代码实现
用于测试的 JavaBean:
package com.huey.pattern.prototype; import java.io.Serializable; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @ToString @NoArgsConstructor @AllArgsConstructor public class TestBean implements Serializable { /** * */ private static final long serialVersionUID = -2351683706932774662L; @Getter @Setter private String testValue; }
抽象原型:
package com.huey.pattern.prototype; /** * the abstract prototype * @author huey * @version 1.0 * @created 2015-11-27 */ public abstract class Prototype implements Cloneable { public Prototype() { System.out.println("Prototype is constructed."); } @Override protected Prototype clone() { try { return (Prototype) super.clone(); } catch (Exception e) { return null; } } }
具体原型:
package com.huey.pattern.prototype; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; /** * the concrete prototype * @author huey * @version 1.0 * @created 2015-11-27 */ @ToString @NoArgsConstructor @AllArgsConstructor public class ShallowCopyBean extends Prototype { @Getter @Setter private Integer primitiveValue; @Getter @Setter private TestBean customValue; }
单元测试:
package com.huey.pattern.prototype; public class ShallowCopyBeanTest { public static void main(String[] args) { Prototype cloneableBean = new ShallowCopyBean(10, new TestBean("AAA")); System.out.println("cloneableBean: " + cloneableBean); ShallowCopyBean clonedBean = (ShallowCopyBean) cloneableBean.clone(); clonedBean.setPrimitiveValue(100); clonedBean.getCustomValue().setTestValue("BBB"); System.out.println("cloneableBean: " + cloneableBean); System.out.println("clonedBean: " + clonedBean); } }
结果输出:
Prototype is constructed. cloneableBean: ShallowCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA)) cloneableBean: ShallowCopyBean(primitiveValue=10, customValue=TestBean(testValue=BBB)) clonedBean: ShallowCopyBean(primitiveValue=100, customValue=TestBean(testValue=BBB))
从输出可以看到,原型只被构造一次。但是这里需要注意当 clonedBean 的引用对象 customVaule 的内容发生改变时,cloneableBean 的 customVaule 也跟着改变。这就是深拷贝与浅拷贝的问题,Java 中,Object 类的 clone 方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
深拷贝
实现深拷贝可以在重写 clone 方法中,对数组、容器对象、引用对象等自己进行拷贝。
还可以使用一个技巧,就是利用序列化的机制来实现,通过将对象序列化到输出流中,然后将其读回,这样产生的新对象是对现有对象的一个深拷贝。
代码实现
可序列化的抽象原型:
package com.huey.pattern.prototype; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * the abstract prototype * @author huey * @version 1.0 * @created 2015-11-27 */ public abstract class SerialCloneable implements Serializable, Cloneable { /** * */ private static final long serialVersionUID = -2477239830471073680L; @Override protected Object clone() { try { /** * serialize the object to the output stream */ ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(bout); output.writeObject(this); output.close(); /** * deserialize the object from the input stream */ ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream input = new ObjectInputStream(bin); Object result = input.readObject(); input.close(); return result; } catch (Exception e) { return null; } } }
具体原型:
package com.huey.pattern.prototype; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; /** * the concrete prototype * @author huey * @version 1.0 * @created 2015-11-27 */ @ToString @NoArgsConstructor @AllArgsConstructor public class DeepCopyBean extends SerialCloneable { /** * */ private static final long serialVersionUID = -959424582381713341L; @Getter @Setter private Integer primitiveValue; @Getter @Setter private TestBean customValue; }
单元测试:
package com.huey.pattern.prototype; public class DeepCopyBeanTest { public static void main(String[] args) { SerialCloneable cloneableBean = new DeepCopyBean(10, new TestBean("AAA")); System.out.println("cloneableBean: " + cloneableBean); DeepCopyBean clonedBean = (DeepCopyBean) cloneableBean.clone(); clonedBean.setPrimitiveValue(100); clonedBean.getCustomValue().setTestValue("BBB"); System.out.println("cloneableBean: " + cloneableBean); System.out.println("clonedBean: " + clonedBean); } }
结果输出:
cloneableBean: DeepCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA)) cloneableBean: DeepCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA)) clonedBean: DeepCopyBean(primitiveValue=100, customValue=TestBean(testValue=BBB))
从输出可以看到,clonedBean 的引用对象 customVaule 的内容发生改变时,不会影响到 cloneableBean 的 customVaule。
原型模式的适用场合
1) 产生对象过程比较复杂,初始化需要许多资源时;
2) 希望框架原型和产生对象分开时;
3) 同一个对象可能会供其他调用者使用访问时。