原型模式即prototype pattern, 你可能在听说过它以前就已经体验过原型模式了. 一些编程语言中也在内部实现了原型模式.
原型模式也是构建型模式的一种, 如果说工厂模式侧重于create的话, 那么原型模式可能更适合用clone来描述. 虽然从实际行为表现上, 原型模式也可以轻松地大批量生成各不相同却又结构类似的实例.
在java中实际上提供了clone()方法用以覆写.
public class Foo implements Cloneable{private String field_1;private Double field_2;// Constructor can be done here or leave it be default@Overrideprotected Object clone(){Foo foo = null;try{// DO SOMETHING...foo = (Foo)super.clone();}catch(Exception e){// DO SOMETHING...}return foo;}}
所以你突然发现, 从前需要使用new来生成对象的时候, 使用clone()方法即可完成.(当然需要相关的类型转换)
实际上不同语言虽然实现clone()方法的途径有所差异, 但最终目的都是希望能够将原型写成一个更高程度上的抽象.这里的抽象不是指非要写抽象类, 而是说原型模式能够做的事情非常多. 你可以在这个译文中了解到更多原型模式的是是非非.
这里则更多地写一些体会和分析,以及一些java实践方面的东西(事实上这其实就是这个知识库的编排本意).
Prototype In Spring
还记得Spring framework吗, 我最开始使用Spring的时候总是觉得虽然xml的配置十分结构化且具体, 然而总是会有一段时间不停地在xml和java代码中周旋. 或许你会说使用注解是更方便的, 但从我的感受来看, xml的方式原始但直观.
在xml里的bean配置里, 有名为scope的属性,你可以通过它来控制IOC容器是以怎样的方式表现beans的, 默认是singleton, 也就是单例, 还可以是prototype, request, session, global session等.
这里的prototype和原型设计模式有什么关系吗? 至少从表现上, 他与单例正好相反, 每次获取到的都是新的对象, 正如你使用clone方法所做的那样. 另外就是和Spring框架相关的, 你会发现容器—也就是通常所说的某个ApplicationContext实现类在调用其close()方法时, 即使指定了销毁方法也不会在容器销毁时销毁prototype的bean(但是JVM还是可以回收). 因此clone这样的行为需要保留一个”原型”是显而易见的.
实际上在BeanFactory的getBean的实现上可以发现原型模式的使用, 尽管它的名字带了”工厂”
浅拷贝还是深拷贝
在java中, 使用clone()方法的话, 对于对象中的字段, 如果是基本类型则复制, 如果是引用类型则只改变指向, 也就是浅拷贝.
但是如果我们真的需要深拷贝, 也就是说: 真正意义上的克隆呢?
那么实现方式可以是:
- 在重写clone方法时做手脚(DO SOMETHING…)
-
clone深拷贝
public class FooDeep implements Cloneable, Serializable{private String field_1;private Bar bar; // An objectprivate Double field_2; // Yes, Double is also an object, but it's bult-in// Constructor can be done here or leave it be defaultpublic FooDeep(){super();}@Overrideprotected Object clone() throws CloneNotSupportException{Object foo = null;foo = super.clone();// Handle with our bar objFooDeep foo_deep = (foo_deep)foo;foo_deep.bar = (Bar)bar.clone();return foo_deep;}}
显然这是在clone方法内部再次将bar克隆, 只要它可以克隆, 那么依赖者FooDeep也可以克隆.
Serialization
序列化其实就是将对象拍扁成像字符串或者字节那样的sequential结构.
在Serialize的方法中, 深拷贝可以这样做(同样以上面的FooDeep为例):
public Object deepClone(){// You defintely want to encapsulate those streamsByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try{// Serializebos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); // 'this' means current classs's instance// Deserializebis = new ByteArrayInputStream(bos.toByteArray());ois = ObjectInputStream(bis);FooDeep foo_deep = (FooDeep)ois.readObject();return foo_deep;}catch(Exception e){return null; // or a vanilla foo_deep}finally{// close, but also can be done with try()bis.close();ois.close();bos.close();ois.close();}}
