原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新对象 。
原型模式 Prototype
所谓原型模式 Prototype['protəˌtaɪp]
,就是指 clone
技术,以某个对象为原型,复制出一个新的对象。
类图结构
结构解析
Prototype
抽象类,原型类申明一个 clone
自身的接口。
ConcretePrototype
实现类,实现一个克隆自身的操作。
client
客户端持有原型类实例。
Java
中的原型模式Cloneable
接口在 Java
中,原型模式的 Prototype
抽象类并不需要我们定义,Cloneable
接口实现了这个功能。先看看这个接口的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface Cloneable {}
源码中 Cloneable
是一个空接口,仅用来标记对象,并没有定义 clone
方法。但是 Java
中所有对象都继承了 Object
类,而 clone()
正是它的标准方法之一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Object { ... protected Object clone () throws CloneNotSupportedException { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException("Class " +getClass().getName()+ "doesn't implement Cloneable" ); } return internalClone(); } private native Object internalClone () ; ... }
实现 Cloneable
接口 从源码中可以看出,如果要使用 Object.clone
则必须实现 Cloneable
接口,否则会抛出 CloneNotSupportedException
异常。
clone
新对象时不会调用构造函数clone
方法最终调用了 native
的 internalClone
方法,而这个方法是直接从堆内存中以二进制流拷贝的,所以产生新对象时并不会调用类的构造方法。
深/浅拷贝 深/浅拷贝主要区别在对类中引用对象的处理:
浅拷贝 shallow copy
浅拷贝在拷贝对象时,并不会拷贝对象里的引用对象,也就是说不拷贝该对象的属性。
深拷贝 deep copy
深拷贝在拷贝对象时,需要手动代码实现 对引用对象的拷贝,也就是把该对象的所有属性也克隆出一份新的。
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 public class ConcretePrototypeShallowCopy implements Cloneable { private String type; private TestObject testObject; public ConcretePrototypeShallowCopy (String type, TestObject testObject) { this .type = type; this .testObject = testObject; } @Override protected ConcretePrototypeShallowCopy clone () throws CloneNotSupportedException { return (ConcretePrototypeShallowCopy)super .clone(); } public void show () { System.out.println(type + ": " + testObject.getName()); } } public class ConcretePrototypeDeepCopy implements Cloneable { private String type; private TestObject testObject; public ConcretePrototypeDeepCopy (String type, TestObject testObject) { this .type = type; this .testObject = testObject; } @Override protected ConcretePrototypeDeepCopy clone () throws CloneNotSupportedException { ConcretePrototypeDeepCopy deepCopy = (ConcretePrototypeDeepCopy)super .clone(); deepCopy.testObject = new TestObject(testObject.getName()); return deepCopy; } public void show () { System.out.println(type + ": " + testObject.getName()); } } public class TestObject { private String name; public TestObject (String name) { this .name = name; } public String getName () { return name; } public void setName (String name) { this .name = name; } } public class TestPrototype { public static void main (String[] args) throws Exception { TestObject testObject = new TestObject("TestObject" ); ConcretePrototypeShallowCopy shallowCopy = new ConcretePrototypeShallowCopy("ShallowCopy" , testObject); ConcretePrototypeShallowCopy shallowCopyClone = shallowCopy.clone(); shallowCopy.show(); shallowCopyClone.show(); ConcretePrototypeDeepCopy deepCopy = new ConcretePrototypeDeepCopy("DeepCopy" , testObject); ConcretePrototypeDeepCopy deepCopyClone = deepCopy.clone(); deepCopy.show(); deepCopyClone.show(); testObject.setName("newTestObject" ); System.out.println("####TestObject change name.####" ); shallowCopyClone.show(); deepCopyClone.show(); } } ShallowCopy: TestObject ShallowCopy: TestObject DeepCopy: TestObject DeepCopy: TestObject ####TestObject change name.#### ShallowCopy: newTestObject DeepCopy: TestObject
在测试用例中,修改 TestObject
对象的值后,两种拷贝表现出来的结果不一样:
浅拷贝 因为引用对象并没有克隆,指向的是同一个地址空间,所以 TestObject
对象修改后,浅拷贝生成的新对象会跟着变化。
深拷贝 深拷贝克隆了所有的引用对象,也就是在新地址中拷贝了引用对象的值,源 TestObject
对象的修改不会对深拷贝产生影响。
总结 原型模式适合在什么场景使用?
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等
初始化时需要非常繁琐的数据准备或访问权限,则可以使用原型模式
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
在实际项目中,原型模式通常和工厂方法模式一起出现,通过 clone
的方法创建一个对象,然后由工厂方法提供给调用者。
参考文档