正文
设计模式之(六)原型模式(ProtoType)
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
认识原型模式
原型模式是比较简单的设计模式。废话不多说,直接看定义: 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 通过实例指定种类,种类就是初始化的类,然后通过拷贝创建对象。先展示一个实现的原型模式的例子
public class Product { private String proID; private String proName; private String proDescption; public String getProID() {
return proID;
} public void setProID(String proID) {
this.proID = proID;
} public String getProName() {
return proName;
} public void setProName(String proName) {
this.proName = proName;
} public String getProDescption() {
return proDescption;
} public void setProDescption(String proDescption) {
this.proDescption = proDescption;
} public void showPro(){
System.out.println("ID:"+this.getProID()+";Name:"+this.getProName()+";Descrip:"+this.getProDescption());
System.out.println("-------------------------------------");
} //关键方法
public Product cloneself(){
Product p = new Product();
p.setProName(this.proID);
p.setProID(this.proID);
p.setProDescption(this.proDescption); return p;
}
} public class Client {
public static void main(String[] args) {
Product pp = new Product();
pp.setProID("P0001");
pp.setProName("玩具一号");
pp.setProDescption("此为玩具一号的初代产品"); pp.showPro(); //拷贝
Product pp2 = pp.cloneself();
pp2.showPro();
pp2.setProDescption("此为玩具一号的升级产品");
pp2.showPro(); //原来的
pp.showPro();
}
} //实行效果
ID:P0001;Name:玩具一号;Descrip:此为玩具一号的初代产品
-------------------------------------
ID:P0001;Name:P0001;Descrip:此为玩具一号的初代产品
-------------------------------------
ID:P0001;Name:P0001;Descrip:此为玩具一号的升级产品
-------------------------------------
ID:P0001;Name:玩具一号;Descrip:此为玩具一号的初代产品
-------------------------------------
通过例子可以看出来,原型模式的核心就是克隆自己的方法,在例子中就是 cloneself,实例对象调用的 cloneself ,就识别出来了对象是哪个类的。同时拷贝了一个新的对象,和原来的实例是完全分离的两个实例。这样实现的原型模式 和 new 比起来有诸多优势,
1)通过拷贝创建对象时,不用关心实例来自哪个类型,只要在例子中实现了拷贝自己的方法即可。
2)通过 new 新建对象时,每个值都是初始状态的,对各个属性,还需要赋值处理。而原型模式在新建了对象时候,就直接获取了原来对象的值,如果有个别属性不一样修改了即可。
通过上面那种方式实现的原型模式,只是在编程方面更灵活。编程效率更高而已,对于执行效果而言,和不用效果是一样的。因为这样实现的模式也是通过new来创建的。有没有更好的克隆方式呢,java 语言里面是有这样的方式的。接下来就介绍 java 实现的 clone 方式。
public class ProtoType implements Cloneable { //......属性值省略 @Override
public ProtoType clone(){
ProtoType pro = null;
try{
pro = (ProtoType)super.clone();
}
catch(CloneNotSupportedException ex){
ex.printStackTrace();
}
return pro;
}
}
上面的代码就是用 java 自己的 clone 实现的克隆。方式就是在 Object 里面有一个 clone 方法,就是用来拷贝对象的,只是使用这个函数需要实现 Cloneable 接口,然后再重写这个方法。为了更好的理解 clone 用法,我们来改造上面的例子。通过 java 语言提供的 clone 来完成这个例子。只是把关键代码贴出来,就是把 原来例子中的 cloneself() 替换成 clone() 。
用 java 自带 clone 改造例子
public class Product implements Cloneable { private String proID; private String proName; private String proDescption; public String getProID() {
return proID;
} public void setProID(String proID) {
this.proID = proID;
} public String getProName() {
return proName;
} public void setProName(String proName) {
this.proName = proName;
} public String getProDescption() {
return proDescption;
} public void setProDescption(String proDescption) {
this.proDescption = proDescption;
} public void showPro(){
System.out.println("ID:"+this.getProID()+";Name:"+this.getProName()+";Descrip:"+this.getProDescption());
System.out.println("-------------------------------------");
} //关键方法
// public Product cloneself(){
// Product p = new Product();
// p.setProName(this.proID);
// p.setProID(this.proID);
// p.setProDescption(this.proDescption);
//
// return p;
// } //关键方法
@Override
public Product clone(){
Product pro = null;
try{
pro = (Product)super.clone();
}
catch(CloneNotSupportedException ex){
ex.printStackTrace();
} return pro;
}
}
通过 java 语言提供的 clone 方法来复制效率很高,主要有一下原因:
1)不用执行构造函数。
2)原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
浅拷贝和深拷贝
浅拷贝:就是只拷贝基本类型的值,比如 int、String 等类型。(这里把 String 类型当成了基本类型)。
深拷贝:除了浅拷贝的值外,还需要拷贝引用类型的对象。
public class Product2 implements Cloneable {
private String name; private ArrayList<String> list = new ArrayList<String>(); public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public ArrayList<String> getList() {
return list;
} public void setList(String str) {
this.list.add(str);
} @Override
public Product2 clone(){
Product2 pro = null;
try{
pro = (Product2)super.clone();
}
catch(CloneNotSupportedException ex){
ex.printStackTrace();
} return pro;
} public void show(){
System.out.println("name="+this.name+" list="+this.list);
}
} public class Client {
public static void main(String[] args) {
Product2 pp = new Product2();
pp.setName("pro2");
pp.setList("pwq");
pp.show(); Product2 pp2 = pp.clone();
pp2.setName("pro222");
pp2.setList("pcf");
pp2.show();
}
} /*****执行效果**********/
name=pro2 list=[pwq]
name=pro222 list=[pwq, pcf]
比较两次的执行效果,克隆后的类名字是分离了,就是成功克隆了;但是 list 这个很明显就是共享了,就是没有克隆成功。这个就是浅克隆。如果需要深克隆,就需要修改下上面代码;
@SuppressWarnings("unchecked")
@Override
public Product2 clone(){
Product2 pro = null;
try{
pro = (Product2)super.clone();
pro.list = (ArrayList<String>)pro.list.clone();
}
catch(CloneNotSupportedException ex){
ex.printStackTrace();
} return pro;
}
加上这句话后,修改为深度拷贝。手动编写的类也是典型的引用类型,深拷贝也是需要重点注意的。
原型管理器
有时候原型可能有多个,而且还不固定,中间可能动态的变化。这个时候对原型的管理就需要维护一个注册表,这个注册表就是原型管理器。在原型注册器中可以添加和销毁。看例子理解
public interface ProtoType {
public String getName(); public void setName(String name); public ProtoType clone();
} public class ConcreteProtoTypeA implements ProtoType{
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public ProtoType clone(){
ConcreteProtoTypeA cA = new ConcreteProtoTypeA();
cA.setName(this.name);
return cA;
}
} public class ConcreteProtoTypeB implements ProtoType{
//..........
} //原型管理器
public class ProtoTypeManager {
private static Map<String,ProtoType> pt = new HashMap<String,ProtoType>();
//不让实例化
private ProtoTypeManager(){} public static synchronized void setProtoType(String protoID,ProtoType p){
pt.put(protoID, p);
} public static synchronized void removeProtoType(String protoID){
pt.remove(protoID);
} public static synchronized ProtoType getProtoType(String protoID){
ProtoType p = null; if(pt.containsKey(protoID)){
p = pt.get(protoID);
}
return p;
} public static void show(){
System.out.println(pt);
}
}
原型管理器
通过这个例子就很清楚的理解了原型管理器是有什么用处了,本质上就是存放多个原型的,并且能够动态的添加、删除、获取。
使用场景
1、需要一个类实例化大量的重复对象,或者数据重复性很大,极个别需要修改的属性。
2、对象初始化过程比较复杂。
3、在运行时刻不方便获取原型的类时,也可以通过原型模式来实现。
小结
原型模式说了这么多,本质就是原型不需要获取到所属种类,通过原型就能够通过克隆自己来创建对象。通过这个本质就能看到引出的优势。而 java 又对这个模式进行了语言级别的支持。Object 的 clone 就能够克隆,只要实现了 cloneable 接口。java 自带的 clone 克隆自己还不需要再次执行构造方法,操作的是内存二进制数据,效率非常的好。