原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式属于创建型模式,前面已经讲了四个创建型模式,原型模式是最后一个。通过定义描述,不难理解原型模式相当于复制对象,避免复杂对象的初始化过程。
从图上看,原型模式非常简单。只要具体类实现Cloneable接口,覆盖Object类下都有的clone方法。clone方法是通过jni调用,虚拟机会调用已实现Cloneable接口的类中的clone方法,所以clone方法必须覆盖,不然会报错。不需要我们知道原理,总之就是复制对象。
protected native Object clone() throws CloneNotSupportedException;
举个汽车的例子。通常汽车都有发动机、轮胎、类型、颜色等等。同个类型的汽车配置大多都是一样的,而也有些区别。比如白色奔驰和黑色奔驰,只是颜色不一样。这时白色奔驰作为参考,再新造黑色奔驰,相当于复制一辆奔驰,改变颜色。
代码示例
public class Car implements Cloneable {
private String type;
private String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
protected Object clone(){//覆盖clone方法
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
客户端
Car car1 = new Car();
car1.setType("奔驰");
car1.setColor("白色");
System.out.println(car1.getColor()+car1.getType()+car1.toString());//输出 白色奔驰Car@659e0bfd
Car car2 = (Car) car1.clone();
car2.setColor("黑色");
System.out.println(car2.getColor()+car2.getType()+car2.toString());//输出 黑色奔驰Car@15db9742
从代码可以看到,car1拷贝car2,car2还带有car1的属性,而且两个对象指向的不是同一个内存地址,所以各自修改也不影响对方。这就是clone作用。
深拷贝
如果我新增一个引擎类,作为Car的属性。引擎名字默认为default。
public class Engine{
private String name = "default";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Car implements Cloneable {
//...
private Engine engine = new Engine();
public Engine getEngine() {
return engine;
}
}
客户端
Car car1 = new Car();
System.out.println(car1.getEngine().getName() + car1.getEngine().toString());
Car car2 = (Car) car1.clone();
car2.getEngine().setName("new");
System.out.println(car2.getEngine().getName() + car2.getEngine().toString());
System.out.println(car1.getEngine().getName() + car1.getEngine().toString());
输出为:
defaultEngine@659e0bfd
newEngine@659e0bfd
newEngine@659e0bfd
可以看出里面的实例都是一样的,引用地址是同一块区域所以当car1的engine改变,car2的engine也跟着改变,这也不符合我们的使用原型初衷。这叫浅拷贝,只能拷贝基本类型不能拷贝对象。当然也有解决方案,有浅就有深,称为深拷贝。做法是需要被拷贝对象实现Cloneable接口,覆盖clone方法。然后在拷贝的地方调用被拷贝对象的clone方法。
public class Engine implements Cloneable {
//...
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Car implements Cloneable {
//...
protected Object clone(){
try {
Car car = (Car) super.clone();
car.engine = (Engine) engine.clone();//调用该属性的clone方法
return car;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
客户端代码不改,编译运行,得出的是我们想要的结果。
总的来说,应用在创建多次重复对象时或者耗时的对象初始化,原型模式可以不受构造器的约束,减少初始化的开销。只要被拷贝的对象实现Cloneable接口并且覆盖clone方法即可。原型模式分为浅拷贝和深拷贝,使用情况取决于内部实例是否重新指定新的内存区域。不足之处望指教。