原型模式
解决问题
当对象的构造函数非常复杂,在生成新对象的时候非常耗时间、耗资源的情况,怎么来创建.
方案
通过复制(克隆、拷贝)一个指定类型的对象来创建更多同类型的对象。这个指定的对象可被称为“原型”对象,也就是通过复制原型对象来得到更多同类型的对象。
结构
客户(Client)角色:使用原型对象的客户程序
抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)
具体原型(Concrete Prototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象。此角色需要实现抽象原型角色所要求的接口。
适用性
当一个系统应该独立于它的产品创建、构成和表示时,建议使用 Prototype模式
当要实例化的类是在运行时刻指定时,例如,通过动态装载;
为了避免创建一个与产品类层次平行的工厂类层次时
当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
优缺点
优点
- 运行时刻增加和删除产品比其他创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
- 改变值以指定新对象,极大的减少系统所需要的类的数目
- 改变结构以指定新对象:许多应用由部件和子部件来创建对象。
- 减少子类的构造 Factory Method 经常产生一个与产品类层次平行的 Creator类层次
缺点
每一个Prototype的子类都必须实现clone操作
注意点
深浅拷贝
- 浅拷贝: 被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
- 深拷贝: 被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
经典实现
浅拷贝 对人员类的拷贝
public class Person implements Cloneable {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("k1",12);
Person p2 = (Person) person.clone();
System.out.println(p2);
}
}
深拷贝
在Person类中添加一个是引用数据类型的成员属性Computer
public class Computer implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String brand;
private String cup;
private Long memory;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
方法一: 重写clone方法,实现深拷贝
public class Person implements Cloneable {
private String name;
private Integer age;
private Computer computer;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 克隆基本数据和String类型数据,与引用类型的地址值
Person person = (Person) super.clone();
//调用引用数据类型的clone方法克隆出一个新的对象,并赋值给新的person类,来替换掉浅拷贝的地址值.
person.computer = (Computer) this.computer.clone();
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("k1",12);
Person p2 = (Person) person.clone();
person.setAge(14);
System.out.println(p2);
}
}
方法二: 采用序列化方法实现深拷贝
public Object deepClone() throws IOException {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
bos.close();
oos.close();
bis.close();
ois.close();
}
return null;
}
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Person person = new Person("k1",12);
person.setComputer(new Computer("华为","i7",16L));
Person p2 = (Person) person.deepClone();
person.setAge(14);
System.out.println(p2);
}