原型模式
意图
用原型实例指定创建对象的种类,并且通过拷贝(注意深拷贝与浅拷贝)这些原型创建新的对象。
拷贝(克隆)
浅克隆(浅拷贝)
浅克隆(ShallowClone):也叫浅拷贝,在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址(一变都变)。
深克隆(深拷贝)
深克隆(DeepClone):也叫深拷贝,在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象(克隆对象的改变不影响原来对象)。
动机
有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。
结构
参与者
适用场景
1、创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
2、需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
3、原型模式也能够用来解决“仅仅知接口而不知实现的问题”,使用原型模式,能够出现一种独特的“接口造接口”的景象,这在面向接口编程中非常实用。
本质
克隆生成对象。克隆是手段。目的还是生成新的对象实例。
优点
- 性能高 : 使用原型模式复用的方式创建实例对象 , 比使用构造函数重新创建对象性能要高 ; ( 针对类实例对象开销大的情况 )
- 流程简单 : 原型模式可以简化创建的过程 , 可以直接修改现有的对象实例的值 , 达到复用的目的 ; ( 针对构造函数繁琐的情况 )
缺点
- 覆盖 clone 方法 ( 必须 ) : 必须重写对象的 clone 方法 , Java 中提供了 cloneable 标识该对象可以被拷贝 , 但是必须覆盖 Object 的 clone 方法才能被拷贝 ;
- 深拷贝 与 浅拷贝 风险 : 克隆对象时进行的一些修改 , 容易出错 ; 需要灵活运用深拷贝与浅拷贝操作
通过简历案例说明原型模式
/**
* 场景:我们需要很多份简历,简历信息可以不相同也可以相同。
* 第一步:创建简历原型Resume类,重写克隆方法
* 第二步:客户端测试通过克隆创建新对象
* 有引用类型数据要用深拷贝
*/
//创建简历原型Resume类,重写克隆方法
public class ResumePrototype implements Cloneable{
private String name;
private Address address;
public ResumePrototype(String name) {
this.name = name;
this.address = new Address();
}
private ResumePrototype(Address address) throws CloneNotSupportedException {
this.address = (Address) address.clone();
}
public void setAddress(String add) {
address.setAdd(add);
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//引用也进行了拷贝
ResumePrototype obj = new ResumePrototype(this.address);
obj.name = this.name;
return obj;
}
@Override
public String toString() {
return "ResumePrototype{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class Address implements Cloneable{
private String add;
public void setAdd(String add) {
this.add = add;
}
@Override
public String toString() {
return "Address{" +
"add='" + add + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//第二步:测试
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
ResumePrototype resume1 = new ResumePrototype("liming");
resume1.setAddress("china");
ResumePrototype resume2 = (ResumePrototype) resume1.clone();
resume2.setAddress("Russia");
System.out.println(resume1);
System.out.println(resume2);
}
}
//结果
ResumePrototype{name='liming', address=Address{add='china'}}
ResumePrototype{name='liming', address=Address{add='Russia'}}