设计模式之原型模式
原型模式:用原型指定创建原型的种类,并且通过拷贝原型创建新的对象。
简单点说:原型模式和工厂模式一样都属于创建型模式,但是工厂模式是根据你的需求创建不同的对象,原型模式是根据原型创建一样的对象(复制拷贝),工厂模式是用new创建对象,原型模式是根据原型克隆创建对象效率更高。
打个比方:流水线上生产华为手机(对象),首先得有一个手机样品模板(即原型),所有的华为手机(对象)都根据这个模板生产。因此一条流水线运行下来,生产的手机都长一样(复制,克隆)。这就是原型模式
为什么要使用原型模式?
原型模式创建对象用的是objec类中clone方法(浅克隆),它操作的是本地方法,效率高,并且new对象不能具备当前运行时的状态,new产生的对象是通过构造方法中的默认值生成;
原型模式通常和工厂模式结合使用:
通常通过原型模式克隆一个对象,然后通过工厂提供给调用者。
原型模式代码示例(浅克隆):
场景:把齐天大圣当作一个对象,然后把齐天大圣的金箍棒也看成一个对象,齐天大圣会持有金箍棒的引用。齐天大圣会变化出千万个自己,这就是一个原型模式,但是发现一个问题,所有齐天大圣持有的金箍棒是同一根,请看下面示例。
1.猴子类:
2.金箍棒类:
3.齐天大圣类:持有金箍棒的引用,并实现cloneable接口
测试:
可以看出clone出来的齐天大圣是不同的对象,但是他们的金箍棒都是同一根。
我们再看一个示例:
原型类:
测试代码:
输出结果:
总结:通过上面代码可以看出,实现Cloneable接口只能实现浅克隆,它能实现基本类型的值进行复制,但是类似集合类型,引用类型它只是复制了对象的引用,指向的还是原有的对象。所有克隆对象中引用类型或集合类型指向的是同一个地址,若对克隆对象属性做修改的时候,可能会影响其它克隆对象,这样是不靠谱的,为了规避这种情况的产生,我们现在来看一下深克隆,它能把对象中的引用也重新克隆。
原型模式深克隆代码示例:
还是引用刚才齐天大圣那个例子,把齐天大圣类的克隆方法重写下:
public class Qitiandasheng extends Monkey implements Serializable,Cloneable { //金箍棒 private JinGuBang jgb=new JinGuBang(); /**\ * 序列化实现深度克隆(反射也能实现) * 把对象的字节码数组读出来,通过字节码重新构造一个对象 * @return */ public Object clone() throws CloneNotSupportedException{ try { ByteArrayOutputStream bos=new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois=new ObjectInputStream(bis); Qitiandasheng qtds=(Qitiandasheng)ois.readObject(); return qtds; }catch (Exception e){ e.printStackTrace(); return null; } } public JinGuBang getJgb() { return jgb; } public void setJgb(JinGuBang jgb) { this.jgb = jgb; } // public Object clone() throws CloneNotSupportedException { // return super.clone(); // } }
测试类:
可以看到两个齐天大圣引用的金箍棒是不相等的。
当然实现深克隆的方式有很多种,我这里用到的是序列化,spring中用到的原型模式大多都是用反射。