设计模式-原型模式
原型模式是什么?
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
通俗的说就是将某一事物当作原型拷贝(克隆)一份,创建一个副本。
第一只克隆羊-多莉
用途
用于再次创建相同的复杂对象,提升效率
实现
1、通过克隆实现
/*
1、实现一个接口 Cloneable
2、重写一个方法 clone()
*/
@Data
public class Vlog implements cloneable{
private String name;
private Date createTime;
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
客户端进行原型克隆
public class XiaoWei{
public static void main (String[] args){
Date date = new Date();
Vlog v1 = new Vlog("小微的博客", date);
Vlog v2 = (Vlog)v1.clone();
/*
v1 == v2???
这里是深克隆还是浅克隆??
*/
date.setTime(1232432);
/*
v1 == v2???
*/
}
}
答案是:这里是浅克隆!!
那么如何实现深克隆??
改造克隆方法!!
public class Vlog implements cloneable{
private String name;
private Date createTime;
protected Object clone() throws CloneNotSupportedException{
Object obj = super.clone();
Vlog v = (Vlog)obj;
//把当前这个对象的属性也克隆~
v.createTime = (Date)this.createTime.clone();
return obj;
}
}
2、通过序列化、反序列化实现
较为复杂,涉及IO
@Data
public class Monkey implements Cloneable, Serializable{
private int height;
private int weight;
private Date birth;
public Monkey (){
this.birth = new Date();
}
public Object deepClone() throws IOException, ClassNotFoundException{
//将对象写到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流中读出来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
public class theMonkeyKing{
public static void main(String[] args){
Monkey monkeyKing = new Mongkey();
Monkey copyMonkeyKing = (Monkey)monkeyKing.deepClone();
System.out.println("俩美猴王是一个对象吗?" + (monkeyKing == copyMonkeyKing));
}
优势&&缺点
优势:
- 程序运行效率:可以节省new创建对象的时间,
- 代码开发效率:可以一个clone()方法搞定,无需复杂的赋值操作
- 代码的稳定性:依次一个一个的进行代码赋值,会不会因为一个Cv操作失误导致一个细微难察觉的代码bug发布出来了呢?
缺点:
原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类可能就会很麻烦,因为要修改原先的代码,还要考虑对象的结构改造。违背了“开闭原则”!!
不使用clone,怎么快速友好,非侵入的实现原型模式
org.springframework.beans.BeanUtils
org.apache.commons.beanutils.BeanUtils
相信大家都用过BeanUtils.copyProperties(),用于对象的拷贝,这里其实就是原型模式的一个应用!!
值得说明的是: spring 与apache的BeanUtils相比,在性能上spring有极大的优势,因此在简单做属性赋值的时候推荐使用spring的,经过测试在大规模的赋值操作中spring性能可以达到apace性能的100倍。
应用场景
- 资源优化场景。类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
- 细胞分裂。 JAVA中的 Object clone() 方法。在Spring中,用户也可以采用原型模式来创建新的bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
- 原型模式应用于很多软件中,如果每次创建一个对象要花大量时间,原型模式是最好的解决方案。很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的应用,复制得到的对象与原型对象是两个类型相同但内存地址不同的对象,通过原型模式可以大大提高对象的创建效率。
- 在Struts2中为了保证线程的安全性,Action对象的创建使用了原型模式,访问一个已经存在的`Action对象时将通过克隆的方式创建出一个新的对象,从而保证其中定义的变量无须进行加锁实现同步,每一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导致的并发和同步问题。
应用举例
- Spring Bean 的创建,引用了单例模式+原型模式
- 西游记里面孙悟空用猴毛变出超多个自己
- 游戏里面的小兵和一些相似的环境布景
- 可口可乐公司生产可乐,应用工厂模式+原型模式