设计模式之原型模式
1 引子
在实际项目中,有时候会复制对象,具体可看下方代码。
可克隆的Person类:
public class Person implements Cloneable {
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
@Override
public Object clone() throws CloneNotSupportedException{
Object object = super.clone();
return object;
}
}
这里继承Cloneable接口,作用在于运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在JVM中,只有实现了Cloneable接口的类才可以被拷贝,否则运行抛CloneNotSupportedException异常。
同时,Object类自带clone(),但其作用域为protected,所以一般很难被调用,这里重写clone方法是为了扩大clone()方法的作用域。
客户端:
public class Client {
public static void main(String[] args) {
Person person1 = new Person("xiaoLi", 20, "m");
Person person2 = null;
try {
person2 = (Person)person1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(person1 + " " + person2);
person1.setName("xiaoMing");
System.out.println(person1 + " " + person2);
}
}
运行结果
Person [name=xiaoLi, age=20, sex=m] Person [name=xiaoLi, age=20, sex=m]
Person [name=xiaoMing, age=20, sex=m] Person [name=xiaoLi, age=20, sex=m]
这种通过克隆原型对象获得另外一个对象的创建模式称为原型模式。
这里讲到对象克隆,就要区分浅拷贝和深拷贝:
先看个例子来说明下浅拷贝和深拷贝。
import java.util.ArrayList;
import java.util.List;
public class City implements Cloneable {
private String name;
private int area;
private List<Person> persons;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getArea() {
return area;
}
public void setArea(int area) {
this.area = area;
}
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
@Override
public String toString() {
return "City [name=" + name + ", area=" + area + ", persons=" + persons
+ "]";
}
public Object clone() throws CloneNotSupportedException {
// 浅拷贝方式
City city = (City)super.clone();
// 深拷贝方式
// List<Person> personNew = new ArrayList<Person>();
// List<Person> persons = city.getPersons();
// for (Person personTemp : persons) {
// personNew.add((Person)personTemp.clone());
// }
// city.setPersons(personNew);
return city;
}
}
public class Client {
public static void main(String[] args) {
Person person1 = new Person("xiaoLi", 20, "m");
Person person2 = new Person("xiaoZhao", 25, "f");
Person person3 = new Person("xiaoQi", 30, "f");
List<Person> persons = new ArrayList<Person>();
persons.add(person1);
persons.add(person2);
persons.add(person3);
City city = new City();
city.setName("beijing");
city.setArea(110);
city.setPersons(persons);
City city2 = null;
try {
city2 = (City)city.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(city);
System.out.println(city2);
city.setName("nanjing");
person1.setAge(100);
System.out.println(city);
System.out.println(city2);
}
}
浅拷贝运行结果:
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=20, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=20, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
=============================================================================
City [name=nanjing, area=110, persons=[Person [name=xiaoLi, age=100, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=100, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
深拷贝运行结果:
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=20, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=20, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
=============================================================================
City [name=nanjing, area=110, persons=[Person [name=xiaoLi, age=100, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
City [name=beijing, area=110, persons=[Person [name=xiaoLi, age=20, sex=m], Person [name=xiaoZhao, age=25, sex=f], Person [name=xiaoQi, age=30, sex=f]]]
结果表明:
①浅拷贝对于对象内部的引用对象只会copy引用,其指向的地址与原型对象内引用对象指向的地址相同。
如上图所示,修改persons列表中的任何一个person元素,都会影响原型对象和浅拷贝对象。对于技术基本数据类型和String类型,则不受任何影响。
②深拷贝对于原型对象中引用对象都会进行拷贝,从代码中也能看出。
因此对原型对象中引用对象的属性age进行修改,不会影响到深拷贝对象的值。对于浅拷贝和深拷贝可参考下方的参考资料。
2 原型模式原理
《大话设计模式》中定义原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。意思就是说:通过clone方式创建新对象就是原型模式。
3 原型模式特点
原型模式有什么优点?
通过clone方法创建新的对象,比直接new一个对象要快。因为克隆对象不需要经过构造方法,直接操作内存中的二进制流,免去了类的初始化工作。
原型模式有什么缺点?
实际原型模式用的并不多,因为涉及到浅拷贝和深拷贝。同时要避免与单例模式混用,因为clone()方法不会通过构造函数来创建对象,而单例模式之所以单例,依赖的就是私有的构造函数,因此使用时应避免与单例模式混用。
4 原型模式使用场景
后续若有落地经验,再来补充。
5 参考资料
《大话设计模式》
https://blog.****.net/jason0539/article/details/23158081
浅拷贝和深拷贝
https://blog.****.net/baiye_xing/article/details/71788741