原型模式即prototype pattern, 你可能在听说过它以前就已经体验过原型模式了. 一些编程语言中也在内部实现了原型模式.
原型模式也是构建型模式的一种, 如果说工厂模式侧重于create的话, 那么原型模式可能更适合用clone来描述. 虽然从实际行为表现上, 原型模式也可以轻松地大批量生成各不相同却又结构类似的实例.

在java中实际上提供了clone()方法用以覆写.

  1. public class Foo implements Cloneable{
  2. private String field_1;
  3. private Double field_2;
  4. // Constructor can be done here or leave it be default
  5. @Override
  6. protected Object clone(){
  7. Foo foo = null;
  8. try{
  9. // DO SOMETHING...
  10. foo = (Foo)super.clone();
  11. }catch(Exception e){
  12. // DO SOMETHING...
  13. }
  14. return foo;
  15. }
  16. }

所以你突然发现, 从前需要使用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深拷贝

    1. public class FooDeep implements Cloneable, Serializable{
    2. private String field_1;
    3. private Bar bar; // An object
    4. private Double field_2; // Yes, Double is also an object, but it's bult-in
    5. // Constructor can be done here or leave it be default
    6. public FooDeep(){
    7. super();
    8. }
    9. @Override
    10. protected Object clone() throws CloneNotSupportException{
    11. Object foo = null;
    12. foo = super.clone();
    13. // Handle with our bar obj
    14. FooDeep foo_deep = (foo_deep)foo;
    15. foo_deep.bar = (Bar)bar.clone();
    16. return foo_deep;
    17. }
    18. }

    显然这是在clone方法内部再次将bar克隆, 只要它可以克隆, 那么依赖者FooDeep也可以克隆.

Serialization

序列化其实就是将对象拍扁成像字符串或者字节那样的sequential结构.
在Serialize的方法中, 深拷贝可以这样做(同样以上面的FooDeep为例):

  1. public Object deepClone(){
  2. // You defintely want to encapsulate those streams
  3. ByteArrayOutputStream bos = null;
  4. ObjectOutputStream oos = null;
  5. ByteArrayInputStream bis = null;
  6. ObjectInputStream ois = null;
  7. try{
  8. // Serialize
  9. bos = new ByteArrayOutputStream();
  10. oos = new ObjectOutputStream(bos);
  11. oos.writeObject(this); // 'this' means current classs's instance
  12. // Deserialize
  13. bis = new ByteArrayInputStream(bos.toByteArray());
  14. ois = ObjectInputStream(bis);
  15. FooDeep foo_deep = (FooDeep)ois.readObject();
  16. return foo_deep;
  17. }catch(Exception e){
  18. return null; // or a vanilla foo_deep
  19. }finally{
  20. // close, but also can be done with try()
  21. bis.close();ois.close();bos.close();ois.close();
  22. }
  23. }