原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
克隆模式的要点是:地址不同,属性相同!
如果一个对象有四个属性,那么我们可以直接通过new实例化对象,但是如果对象有四十个属性呢?这就是克隆模式发挥光和热的时候了。
首先假定一个商品类,如下:
package Clone;
public class Goods {
private String code;
private Integer price;
private String name;
public Goods() {
}
public Integer getPrice() {
return price;
}
public String getCode() {
return code;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(Integer price) {
this.price = price;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
}
然后我们有两个商品,一个是头铁NB到发光的 “康帅傅牌方便面” 另一个是普普通通的 “康师傅方便面”,如下:
package Clone;
public class Main {
public static void main(String[] args){
Goods goods1=new Goods();
goods1.setCode("A001");
goods1.setName("康帅傅牌方便面");
goods1.setPrice(3);
Goods goods2=goods1;
goods2.setCode("A002");
goods2.setName("康师傅方便面");
//输出
System.out.println(goods1.getCode());
System.out.println(goods2.getCode());
System.out.println(goods1.getName());
System.out.println(goods2.getName());
System.out.println(goods1.getPrice());
System.out.println(goods2.getPrice());
}
}
这里我们通过直接复制的方式获得了两个实例,那么最后的运行结果如何呢?
A002
A002
康师傅方便面
康师傅方便面
3
3
毫不意外,两个实例的输出结果都是一样的,即使我们在对goods2实例的属性进行了修改。由此可见,这并不符合克隆模式的要求,因为goods2和goods1的内存地址都特么是一样的(这也是为什么修改了goods2,goods1也莫名其妙被改变的原因,学习过C++的同学可以很容易理解)。
解决办法是什么呢?首先介绍浅拷贝,如下:
package Clone;
public class Goods implements Cloneable {
private String code;
private Integer price;
private String name;
public Goods() {
}
public Integer getPrice() {
return price;
}
public String getCode() {
return code;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(Integer price) {
this.price = price;
}
public void setCode(String code) {
this.code = code;
}
@Override
protected Goods clone() {
try {
return (Goods) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public String getName() {
return name;
}
}
A001
A002
康帅傅牌方便面
康师傅方便面
3
3
clone方法是通过实现Cloneable接口得到的,通过这个方法,我们可以得到一份浅拷贝的goods1。什么是浅拷贝,就是对象的所有属性都与拷贝后的对象相同,但是浅拷贝不会拷贝更深层次的东西。也就意味着如果你的某一属性是一个对象,那么浅拷贝将直接拷贝其地址,而不是新建一个对象。
package Clone;
public class Cover {
private String address;
private String filename;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}
package Clone;
import Test.C;
public class Main {
public static void main(String[] args){
Goods goods1=new Goods();
goods1.setCode("A001");
goods1.setName("康帅傅牌方便面");
goods1.setPrice(3);
goods1.getCover().setAddress("D:/image/");
goods1.getCover().setFilename("A001.jpg");
Goods goods2=goods1.clone();//改动在这里
goods2.getCover().setFilename("A002");
goods2.getCover().setAddress("E:/ABC/");
//输出
goods2.setCode("A002");
goods2.setName("康师傅方便面");
System.out.println(goods1.getCode());
System.out.println(goods2.getCode());
System.out.println(goods1.getName());
System.out.println(goods2.getName());
System.out.println(goods1.getPrice());
System.out.println(goods2.getPrice());
System.out.println(goods1.getCover().getAddress());
System.out.println(goods1.getCover().getFilename());
System.out.println(goods2.getCover().getAddress());
System.out.println(goods2.getCover().getFilename());
}
}
输出结果:
A001
A002
康帅傅牌方便面
康师傅方便面
3
3
E:/ABC/
A002
E:/ABC/
A002
深拷贝方案:对特殊属性单独进行拷贝
package Clone;
public class Cover implements Cloneable {
private String address;
private String filename;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
@Override
public Cover clone() {
try {
return (Cover) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
package Clone;
public class Goods implements Cloneable {
private String code;
private Integer price;
private String name;
private Cover cover=new Cover();
public Goods() {
}
public Integer getPrice() {
return price;
}
public String getCode() {
return code;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(Integer price) {
this.price = price;
}
public void setCode(String code) {
this.code = code;
}
@Override
public Goods clone() {
Goods goods=null;
try {
goods=(Goods) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
assert goods != null;
this.cover= goods.cover.clone();
return goods;
}
public String getName() {
return name;
}
public Cover getCover() {
return cover;
}
public void setCover(Cover cover) {
this.cover = cover;
}
}
A001
A002
康帅傅牌方便面
康师傅方便面
3
3
D:/image/
A001.jpg
E:/ABC/
A002