设计模式 -- 原型模式

112 阅读4分钟

当克隆优于创建

原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。

场景

  • 当你的系统并不想关心一个对象的创建,组合的细节.
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
  • 当一个类的实例只有几种状态的时候,先创建好这几种实例,需要时clone,好过每次创建新的,然后设置到合适的状态.
  • 当创建对象的代价大于克隆

用法

原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制新的实例出来.
clone是Object类的方法,其实原型模式在某种程度上和java融为一体.
有时原型模式和工厂模式结合在一起,用于为工厂创造新的对象.
在原型模式的使用中,浅拷贝和深拷贝是需要注意的地方.

克隆

Java的所有类都是从java.lang.Object类继承而来的,而Object类提供protected Object clone()方法对对象进行复制,子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法。对象的复制有一个基本问题,就是对象通常都有对其他的对象的引用。当使用Object类的clone()方法来复制一个对象时,此对象对其他对象的引用也同时会被复制一份.

Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。

克隆满足的条件:
clone()方法将对象复制了一份并返还给调用者。“复制”的含义即clone()方法是怎么实现的。
一般而言,clone()方法满足以下的要求:

  1. 对任何的对象x,都有:x.clone()!=x。换言之,克隆对象与原对象不是同一个对象。
  2. 对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原对象的类型一样。
  3. 如果对象x的equals()方法定义其恰当的话,那么x.clone().equals(x)应当成立的。
    在JAVA语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件。JAVA语言的设计师在设计自己的clone()方法时,也应当遵守着三个条件。一般来说,上面的三个条件中的前两个是必需的,而第三个是可选的。

浅拷贝

只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象.
换言之,所有的对其他对象的引用都仍然指向原来的对象
Object默认的clone方式是这样做的.

深拷贝

除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
深拷贝一般有两种方式:

  1. 通过实现Cloneable接口,复写clone方法,我们可以自定义一个对象的克隆.
  2. 通过序列化和反序列化实现克隆.
    这样做的前提就是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。