设计模式[3]-原型模式
原型模式
概念
原型模式将一个已经创建的实例作为原型,复制出一个和原型相同的新对象。
包括三种角色:
- 抽象原型:抽象角色,提供具体原型需要实现的接口
- 具体原型:被复制的对象,实现抽象原型的接口
- 客户端:发出创建对象的请求。
例子:
先看一个简单的例子,我们设计一个Cloneable接口作为抽象原型,一个Dog类作为具体原型,Dog类关联一个Color类,然后用测试类做客户端来准备复制对象。
Cloneable:
public interface Cloneable<T> {
T clone();
}
Color:
@Data
public class Color {
private String name;
public Color(String name) {
this.name = name;
}
}
Dog:
@Data
public class Dog implements Cloneable<Dog> {
private String name;
private Color color;
public Dog() {
System.out.println("Dog 构造函数。");
}
@Override
public Dog clone() {
Dog dog = new Dog();
dog.setColor(this.color);
dog.setName(this.name);
return dog;
}
}
DogTest:
class DogTest {
@Test
void show() {
Dog dog = new Dog();
dog.setName("小白");
dog.setColor(new Color("白色"));
Dog dog2 = dog.clone();
System.out.println(dog == dog2);
System.out.println(dog.getColor() == dog2.getColor());
System.out.println("dog:" + dog.getColor().getName());
System.out.println("dog2:" + dog2.getColor().getName());
dog.getColor().setName("黑色");
System.out.println("dog:" + dog.getColor().getName());
System.out.println("dog2:" + dog2.getColor().getName());
}
}
输出:
Dog 构造函数。
Dog 构造函数。
false
true
dog:白色
dog2:白色
dog:黑色
dog2:黑色
分析
首先,我们创建了一个Cloneable接口,然后创建Dog类的实现实现这个接口 ,重写clone()方法,来表示这个类是采用原型模式,这个类可以被复制。
具体的复制方法,在clone()方法中,实例化了Dog类,并赋值给新类。
从DogTest中我们发现了,dog实例对象和复制出来的dog2实例对象是不相等的,说明这是两个类,但是,我们发现了,dog.getColor() 和 dog2.getColor()其实指向的是同一个地址。这就是浅拷贝,只拷贝了数值或者对象的地址,这种情况下,对dog的color做出修改会影响到dog2的color对象的内容。
改进
首先,可以将Color类也实现Cloneable接口,重写clone接口。
@Data
public class Color implements Cloneable<Color>{
private String name;
public Color(String name) {
this.name = name;
}
@Override
public Color clone() {
return new Color(this.name);
}
}
然后,修改Dog类的clone方法,对Color的对象进行clone。
Dog:
@Override
public Dog clone() {
Dog dog = new Dog();
// dog.setColor(this.color);
dog.setColor(this.color.clone());
dog.setName(this.name);
return dog;
}
最后执行测试类,输出:
Dog 构造函数。
Dog 构造函数。
false
false
dog:白色
dog2:白色
dog:黑色
dog2:白色
结果第四行 dog.getColor() == dog2.getColor()
的输出是false,第7行,dog改变颜色变成黑色,没有影响到第8行dog2的白色。拷贝成功。
拓展
java中也有用到原型模式,java.lang.Object 中就有 clone 方法,所以一个对象只需要实现 java.lang.Cloneable接口,然后调用Object类中的clone方法,即可完成对象的拷贝。但是Object类中的clone方法是浅拷贝,而且实现并不是通过像上面的例子一样,通过构造方法直接实例化一个对象出来。
利用自带的Clone方法,实现浅拷贝,代码如下:
@Data
public class Dog implements java.lang.Cloneable {
private String name;
private Color color;
public Dog() {
System.out.println("Dog 构造函数。");
}
@Override
public Dog clone() throws CloneNotSupportedException {
return (Dog)super.clone();
}
}
深拷贝利用Color类的clone方法,或者通过对象的序列化实现,就不细说了。