原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
定义看起来有点绕口,实际上在Java中,Object的clone()方法就属于原型模式,不妨简单的理解为:原型模式就是用来克隆对象的。
举个例子,比如有一天,周杰伦到奶茶店点了一份不加冰的奶茶,你说我是周杰伦的忠实粉,也要一份跟周杰伦一样的,用程序表示如下:
public class MilkTea {
public String type;
public boolean ice;
}
下单:
private void order(){
MilkTea milkTeaOfJay = new MilkTea();
milkTeaOfJay.type = "原味";
milkTeaOfJay.ice = false;
MilkTea yourMilkTea = milkTeaOfJay;
}
好像没有什么问题,将周杰伦的奶茶直接赋值到你的奶茶上就行了,看起来我们并不需要clone方法,但是这样真的是复制一份奶茶吗?
当然不是,Java并非基本类型对象的赋值只是传递地址,这样赋值之后,yourMilkTea仍然指向的周杰伦的奶茶,并不会多一份一样的奶茶。
那么我们要怎么做才能点一份一样的奶茶呢?
private void order(){
MilkTea milkTeaOfJay = new MilkTea();
milkTeaOfJay.type = "原味";
milkTeaOfJay.ice = false;
MilkTea yourMilkTea = new MilkTea();
yourMilkTea.type = "原味";
yourMilkTea.ice = false;
}
只有这样,yourMilkTea才是new出来的一份全新的奶茶,我们设想一下,如果有一千个粉丝都需要点和周杰伦一样的奶茶,按照现在的写法就需要new一千次,并为每一个新的对象赋值一千次,造成大量的重复。
更糟糕的是,如果周杰伦临时决定加冰,那么粉丝的配置也要跟着修改。
private void order(){
MilkTea milkTeaOfJay = new MilkTea();
milkTeaOfJay.type = "原味";
milkTeaOfJay.ice = true;
MilkTea yourMilkTea = new MilkTea();
yourMilkTea.type = "原味";
yourMilkTea.ice = true;
// 将一千个粉丝的 ice 都修改为 true
...
}
大批量的修改无疑是非常丑陋的写法,这就是我们需要clone方法的原因。
运用原型模式,在MilkTea中新增clone方法:
public class MilkTea{
public String type;
public boolean ice;
public MilkTea clone(){
MilkTea milkTea = new MilkTea();
milkTea.type = this.type;
milkTea.ice = this.ice;
return milkTea;
}
}
下单:
private void order(){
MilkTea milkTeaOfJay = new MilkTea();
milkTeaOfJay.type = "原味";
milkTeaOfJay.ice = false;
MilkTea yourMilkTea = milkTeaOfJay.clone();
// 一千位粉丝都调用 milkTeaOfJay 的 clone 方法即可
...
}
这就是原型模式,Java有一个语法糖,让我们并不需要手写clone方法,这个语法糖就是Cloneable接口,我们只要让需要拷贝的类实现这个接口即可。
public class MilkTea implements Cloneable{
public String type;
public boolean ice;
@NonNull
@Override
protected MilkTea clone() throws CloneNotSupportedException {
return (MilkTea) super.clone();
}
}
值得注意的是,Java自带的clone方法是浅拷贝的,也就是说调用此对象的clone方法,只有基本类型的参数会被拷贝一份,非基本类型的参数不会被拷贝一份,而是继续使用传递引用的方式,如果需要实现深拷贝,必须要自己手动修改clone方法才行。