写这期原型模式时,心里是很忐忑的。因为前两天996的原因,精神不是很好。在看了别人写的模式介绍时,觉得大师们有自己的深刻理解,总说一些我没有听过的概念。就没有了写下去的勇气了。其实,就是从了心,怂了。但是一切的理所当然不都是自己一股劲儿闯出来的吗。所以,依然还是写了。
以下,个人理解的原型模式。
原型模式,如果究其字面的意思,就是有一个母体,别的类都是通过它克隆出来的。我就把关键词克隆记住,觉得在以后回忆的时候会更快想起这是一个什么样的模式。
万物有其源,比方说我就是想吃个包子,我可以自己学和面,和馅儿,再上锅去蒸。甚至说我是世界上第一个会做包子的,我的第一个包子就是这个世界所有包子的prototype。而我就是一个构造器。那么别人想吃包子,不用自己包,可以在我这买,那和我的第一个包子类似的其它小包子就诞生了。在创建类上这样做就很高效。原型模式其实也反映了我们社会的一种活动形式-借用别人的成果,相互依赖。单打独斗带来的收获不会很高。反正我是不会为了早上的一盒牛奶去内蒙养牛的。绝不。
我一般喜欢抽出关键词来说明重点。这里的重点是 原型类(prototype),克隆(clone),client(使用者) 这三个就是葵花宝典里的第一章了-最精要的部分。如果配合其它的设计模式,还可以加上一个管理者(manager)的角色。
prototype是个苦命的人儿,什么苦活,累活都是她的。当需要一个对象时,最常见的就是new 一个出来。不是什么事儿都轻而易举的。你想拉个shit,你只要用力new 一个Shit对象就行,不限时间,地点,你开心就行。但是你要吃个饭,要自己做,就得买菜,煮菜;出去吃,得开车,得化妆,这是一个花时间的过程。这就是建立一个prototype会遇到的情况。这个流程不可避免。
但是如果个个在new的时候,都要一顿操作猛如虎,那就不是我们追求的了。
所以,我们要学会克隆(这个要像某讯学习,这个模式,他们学得最好,copycat的代言人),通过克隆或者拷贝获得一个和原型一样的新对象。把原来prototype经历的那些痛苦都免了。吃现在成的。
在极其考验性能的场景,这是很宝贵的。
下面,代码说明。
/**
* 原型类 所有的类型都以它作为原型创建
* 实现Clonable是这个模式的关键, 使用 jkd 11 编辑
*/
public class VehiclePrototype implements Cloneable, Serializable {
private String brand;
private String name;
private Double price;
public VehiclePrototype() {
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "VehiclePrototype{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
/**
* 两种克隆模式 浅拷贝仅复制内部对象地址;若要隔绝类之间的数据冲突,不要使用浅拷贝;这个坑可不浅;
*/
public VehiclePrototype copy() {
try {
return (VehiclePrototype) this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
//
}
return null;
}
/**
* 深拷贝 这是目前我能找到的百分百有效的深拷贝实现;可以将目标类和原型类的所有属性进行隔离;不相互影响;一个字,稳
* 高能!!!!!!序列化你不实现Serializable可不行
*/
public VehiclePrototype deepCopy() {
//使用数组流进行对象的序列化存储
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//使用对象输出流将当前对象写出到数组流中
try {
// 这个写法建议朋友们自己手写一次;第一次记的时候会有点绕;我一般会把带有input的当成自己;这些stream操作的就是进食;带有Output的就是那个往外排(想象那个画面吧);这样时间一久,我也能快速回忆起他们的用法;这个是我的记忆方法
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
//关闭流;别忘了
oos.close();
ois.close();
return (VehiclePrototype) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
这里我只写了一个原型类,现实中很多的情况是有多个子类的,这里的车可以有卡车,汽车等等。最为重要的一点是这里代码实现的细节,原理呢就是通过深拷贝或浅拷贝实现,但是呢,手动撸一次你会发现我在注释中说到的一些问题。要记得住才行。
下面是对深浅拷贝的一个实现对比。这里就涉及到java基础的东西了。关于clone和序列化方式带来的不同拷贝效果的对比。
/**
* 这是demo类,来看看这个模式下怎么做到高效的生成新对象的
*/
public class Client {
public static void main(String[] args) {
// 自己可以想象用最原始的方法创造了一个原型类;可能花费了1分钟;但是后面要用到这样的类千万个
VehiclePrototype prototype = new VehiclePrototype();
//先给这个原型打上一些标记
prototype.setBrand("AUDI");
prototype.setName("A8L");
prototype.setPrice(40_0000.00);
System.out.println("prototype: before " + prototype);
//立马用两个方法进行拷贝操作
VehiclePrototype copycat = prototype.copy();
copycat.setBrand("ALTO");
copycat.setName("NINE_HAND");
copycat.setPrice(3000.00);
//这个是深拷贝
VehiclePrototype lamborghini = prototype.deepCopy();
lamborghini.setName("GALLARDO");
lamborghini.setBrand("LAMBORGHINI");
lamborghini.setPrice(500_0000.00);
// prototype: before VehiclePrototype{brand='AUDI', name='A8L', price=400000.0}
// prototype: after1 VehiclePrototype{brand='AUDI', name='A8L', price=400000.0}
// copycat: before VehiclePrototype{brand='ALTO', name='NINE_HAND', price=3000.0}
// lamborghini: before VehiclePrototype{brand='LAMBORGHINI', name='GALLARDO', price=5000000.0}
// prototype: after2 VehiclePrototype{brand='AUDI', name='new Name', price=400000.0}
// copycat: after VehiclePrototype{brand='ALTO', name='NINE_HAND', price=3000.0}
// lamborghini: after VehiclePrototype{brand='LAMBORGHINI', name='GALLARDO', price=5000000.0}
}
}
那这里只是说明了原型模式所使用的关键代码,但是从大局来说,这样还不行。
从整体上来说,我的理解是原型模式很像一个可以快速拿到货物的仓库,比如说某东的仓库(虽然这两天某东处于风口浪尖,但是他的物流还行)。如果说电脑是一个原型,那我可以把小米的电脑,华硕的电脑,神舟的电脑作为它的子类,它们也是原型啊。不光这样,我设计一个叫ProtypeFactory的工厂,可以根据条件查询到对应的原型。因为这里的原型的获取方式更快了,这就带来了效率的提升。所以,我们在学习使用设计模式的时候不能一招定死,要和其它模式联合使用。打拳的如果一次出手只用一路拳法,是没法赢的。
作为一个建造类型的模式,原型模式节约了获取对象的时间。但是也要考虑到子类一旦增多后,所带来的属性等的扩展成本。
理解有限,这是我看过了别人的说明后,自己又写了两遍代码的感悟。算不上有深度,但又让我的认识上了一个高度。
还是那句话,看过别人说明之后,一定要用自己的语言再把这个内容给教给别人一遍,等到你说明白这个概念所用的时间越来越少的时候,就真的掌握它了(这可是现在世界上最好的学习法了,小伙伴们用起来~!)。如果文中你发现了一些错误,还请指正,毕竟,我的认知也是有限的。
我是杰森小哥,每天学习一点儿,才能看到更大的世界~~