什么是原型模式
原型模式(Prototype Pattern)是五种创建型模式的其中一种,用原型实例指定创建对象的种类作为原型,并且通过拷贝原型来创建新的对象。
为什么要使用原型模式
新建一个对象有时候会很麻烦,可能涉及大量的变量初始化,函数、代码块的执行,不仅浪费资源,还会涉及数据准备、访问权限等操作。
实现
原型模式至少涉及两个角色:
- Prototype,即原型类:需要实现Cloneable接口,并重写clone()方法,为外部提供一个克隆自身的方法;
- Client,即应用类:让一个原型克隆自身从而创建一个新的对象。
public class Prototype implements Cloneable {
//自身的一些方法...
@Override
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new Prototype();
Prototype prototype1 = (Prototype) prototype.clone();
...
}
}
为了探讨其中的一些细节,我们对上面的代码稍微添加一些细节部分:
public class Prototype implements Cloneable {
private String name;
public Prototype() {
System.out.println("empty constructor--------------------");
}
public Prototype(String name) {
System.out.println("constructor--------------------");
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return super.toString() + " " + this.getName();
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new Prototype();
prototype.setName("111");
System.out.println(prototype.toString());
Prototype prototype1 = (Prototype) prototype.clone();
System.out.println(prototype1.toString());
prototype1.setName("222");
System.out.println(prototype.toString());
System.out.println(prototype1.toString());
}
}
在Prototype类中加入了构造函数,重写了toString(),运行main(),结果如下:
empty constructor--------------------
com.company.Prototype@4554617c 111
com.company.Prototype@74a14482 111
com.company.Prototype@4554617c 111
com.company.Prototype@74a14482 222
可以看出:
构造函数只执行了一次,说明复制原型类的对象并不需要执行构造函数;
原对象和复制对象的地址不同,说明是重新创建的;
目前看来,在复制完成之后,复制的对象似乎和原对象就没有关系了,原对象的改变不会影响复制的对象,复制的对象改变也不会影响原对象。
真的是这样吗?
我们修改一下代码:
public class Car {
private String color;
public Car() {
}
public Car(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Prototype implements Cloneable {
private String name;
private Car car;
public Prototype() {
System.out.println("empty constructor--------------------");
}
public Prototype(String name) {
System.out.println("constructor--------------------");
this.name = name;
}
public Prototype(String name, Car car) {
this.name = name;
this.car = car;
}
@Override
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return super.toString() + " " + this.getName() + " " + this.getCar().getColor() + " Car" + " " + this.getCar().toString();
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new Prototype();
Car car1 = new Car("red");
Car car2 = new Car("yel");
prototype.setName("111");
prototype.setCar(car1);
System.out.println(prototype.toString());
Prototype prototype1 = (Prototype) prototype.clone();
System.out.println(prototype1.toString());
prototype1.setName("222");
prototype1.setCar(car2);
System.out.println(prototype.toString());
System.out.println(prototype1.toString());
}
}
执行结果:
empty constructor--------------------
com.company.Prototype@4554617c 111 red Car com.company.Car@74a14482
com.company.Prototype@1540e19d 111 red Car com.company.Car@74a14482
com.company.Prototype@4554617c 111 red Car com.company.Car@74a14482
com.company.Prototype@1540e19d 222 yel Car com.company.Car@677327b6
可以看过结果中第4行和第5行,Car的颜色和地址都不一样,我几乎可以得出结论它们确实不是一个对象了,但是这样写其实是有漏洞的,我通过setCar()是显式地改变了这个对象,不能正确得出结论。
再将输出改一下:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype = new Prototype();
Car car1 = new Car("red");
prototype.setName("111");
prototype.setCar(car1);
System.out.println(prototype.toString());
Prototype prototype1 = (Prototype) prototype.clone();
System.out.println(prototype1.toString());
prototype1.setName("222");
prototype1.getCar().setColor("black");
System.out.println(prototype.toString());
System.out.println(prototype1.toString());
}
}
结果变为:
empty constructor--------------------
com.company.Prototype@4554617c 111 red Car com.company.Car@74a14482
com.company.Prototype@1540e19d 111 red Car com.company.Car@74a14482
com.company.Prototype@4554617c 111 black Car com.company.Car@74a14482
com.company.Prototype@1540e19d 222 black Car com.company.Car@74a14482
所有的Car对象的地址都是同一个,颜色值也一同改变,说明clone()实现的是浅拷贝。
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型仍指向的还是原对象所指向的地址。 深复制:将一个对象复制后,基本数据类型和引用类型都重新创建。
如果想对引用类型也实现深拷贝,可以采用二进制流读写等操作实现,此处不再详述。