设计模式小结-原型模式
原型模式小结
原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的典型应用
主要优点
(1) 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
(2) 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
(3) 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
主要缺点
(1) 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。
(2) 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
适用场景
(1) 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
(2) 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
(3) 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
两种克隆方式
根据克隆对象的成员变量不同,可将克隆方式分为浅克隆和深克隆。
浅克隆
在浅克隆中,如果原型对象的成员变量是值类型(String,int,long.etc..),将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址
简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制,如图所示:
深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制,如图所示:
深克隆使用的对象必须实现Serializable接口
Java中使用clone方法步骤
- 1.实现Cloneable接口
Cloneable为空接口,只是为了将对象标记为可克隆对象
- 2.复写clone方法,声明为public,并返回当前对象类型,在方法中调用super.clone()获取对象.
示例
一个客户类Customer,其中包含一个名为客户地址的成员变量,客户地址的类型为Address,用浅克隆和深克隆分别实现Customer对象的复制并比较这两种克隆方式的异同。
UML类图
示例代码
浅克隆
public class Address {
private String addressName;
public String getAddressName() {
return addressName;
}
public void setAddressName(String addressName) {
this.addressName = addressName;
}
}
public class Customer implements AbstractCustomer {
private String name;
private int age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
//浅克隆方法,只能复制基本类型
@Override
public AbstractCustomer clone() {
AbstractCustomer customer = null;
try {
customer = (AbstractCustomer) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("不支持复制");
e.printStackTrace();
}
return customer;
}
@Override
public void display() {
System.out.println("Customer{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}');
}
}
深度克隆
public class AddressDeepClone implements Serializable{
private String addressName;
public String getAddressName() {
return addressName;
}
public void setAddressName(String addressName) {
this.addressName = addressName;
}
}
public class CustomerDeepClone implements AbstractCustomer, Serializable {
private String name;
private int age;
private AddressDeepClone address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public AddressDeepClone getAddress() {
return address;
}
public void setAddress(AddressDeepClone address) {
this.address = address;
}
@Override
public AbstractCustomer clone() {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
ByteArrayInputStream bis;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(bao);
oos.writeObject(this);
bis = new ByteArrayInputStream(bao.toByteArray());
ois = new ObjectInputStream(bis);
return (AbstractCustomer) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void display() {
System.out.println("CustomerDeepClone{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}');
}
}
客户端调用
Customer c1 = new Customer();
Address address = new Address();
address.setAddressName("广州");
c1.setName("张三");
c1.setAge(22);
c1.setAddress(address);
Customer c2 = (Customer) c1.clone();
System.out.println("客户的地址对象是否相同?" + (c1.getAddress() == c2.getAddress()));
System.out.println("客户对象是否相同?" + (c1 == c2));
CustomerDeepClone customerDeepClone1 = new CustomerDeepClone();
AddressDeepClone addressDeepClone = new AddressDeepClone();
addressDeepClone.setAddressName("上海");
customerDeepClone1.setName("李四");
customerDeepClone1.setAge(24);
customerDeepClone1.setAddress(addressDeepClone);
CustomerDeepClone customerDeepClone2 = (CustomerDeepClone) customerDeepClone1.clone();
System.out.println("深度克隆客户的'地址'对象是否相同?" + (customerDeepClone1.getAddress() == customerDeepClone2.getAddress()));
System.out.println("深度克隆客户对象是否相同?" + (customerDeepClone1 == customerDeepClone2));
输出结果
客户的地址对象是否相同?true
客户对象是否相同?false
深度克隆客户的'地址'对象是否相同?false
深度克隆客户对象是否相同?false