设计模式之原型模式
一、模式定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二、为什么要使用原型模式
当创建给定类的实例的过程很昂贵或很复杂时,就使用原型模式。
三、 哪些情况下可以考虑使用原型模式
- 构建复杂对象时,构建的过程很复杂,或者构建对象代价很高。
- 对象的很多属性可以公用,只有极少部分的属性需要单独赋值。
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
三、怎么实现
java 里面,通过克隆(clone)来实现。
clone ,也分为浅拷贝和深度拷贝。
浅拷贝:直接拷贝,如果是值,就拷贝值,如果是其他对象的引用,直接拷贝引用。引用仍指向原来的对象。
深拷贝:复制引用对象的变量,并指向复制出来的新变量。
四、优缺点
优点:
- 向客户影藏制造新实例的复杂性。
- 提供让客户能够产生未知类型对象的选项。
- 在某些环境下,复制对象比创建新对象更有效。
缺点:
- 对象的复制有时相当复杂。
五、代码示例
浅拷贝
public class Product implements Cloneable {
private String name;
private String desc;
private Source source = new Source();
public Product(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Product() {
}
public void setSource(String name, String desc) {
this.source.setName(name);
this.source.setDesc(desc);
}
// 这里的返回类型是 Product,覆盖了超类的 Object 返回类型,是合法的,也是合理的
// 永远不要让客户端去做任何类库能代替客户端完成的事
// super.clone();简单调用超类的clone是浅拷贝,直接拷贝引用
@Override
protected Product clone() throws CloneNotSupportedException {
Product result = (Product) super.clone();
return result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
", source=" + source +
'}';
}
}
// 客户端调用
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Product productA = new Product("productA","productDescA");
productA.setSource("brotherA","brotherDescA");
Product productB = productA.clone();
productB.setSource("brotherB","brotherDescB");
productB.setName("productB");
System.out.println(productA.toString());
System.out.println(productB.toString());
}
}
得到结果:
Product{name='productA', desc='productDescA', source=Source{name='brotherB', desc='brotherDescB'}}
Product{name='productB', desc='productDescA', source=Source{name='brotherB', desc='brotherDescB'}}
可以很容易看出,如果clone方法只是简单调用 super.clone,则是浅拷贝,如果拷贝的是其他对象的引用,仅仅拷贝了引用,这会造成很严重的后果。
再来看看深度拷贝:
@Override
protected Product clone() throws CloneNotSupportedException {
Product result = (Product) super.clone();
result.source = source.clone();
return result;
}
再执行上述客户端代码,可以看到
Product{name='productA', desc='productDescA', source=Source{name='brotherA', desc='brotherDescA'}}
Product{name='productB', desc='productDescA', source=Source{name='brotherB', desc='brotherDescB'}}