设计模式:原型模式
设计模式:原型模式
一、原型模式简介
关于原型模式,我们可以理解为对象的复制,其实最好就叫做复制对象,这样既清晰又明了,可是学术语言就是要你难以理解,模棱两可,学术,非通俗也。基于此我们也知道了什么叫做原型模式了,所谓原型,就是以什么为基本,比如我们去复印身份证,身份证就是我们的原型,但是身份证只有一个,如果我们还想拿去做其他用途,就只能复印了,当我们复印出了一个身份证的照片之后,总是有一些区别的,比如材质等,这些放到原型模式中,就分为深拷贝和浅拷贝了,当按照原型复制出来一个实体之后,我们总要使用的,具体做什么用,和原型就没关系了,可能在复印纸上写点字(修改对象参数),这样并不会影响原来的身份证(深拷贝)。所以说复印是增加了存储空间的(重新分配空间),当然我们也可能拿着身份证直接去办一件事,这样其实也勉强能看做浅拷贝了。例子总是类似而不能全部相同的,大致明白即可。那么为什么需要原型呢,那是因为有时候我们难以使用一个类创造对象(对象的特征比较多,但也类似,一个类不能兼容并包)或者这样做非常的麻烦(比如我们想要的是 创造出来一个对象并且经过了很多的赋值和运算之后的结果作为对象)。这样做可以使得生成实例的框架不依赖与具体的类(使用框架使得一个类有很多不同表现方式的对象,后面再说)。
二、原型模式代码
Product接口:
1 package zyr.dp.prototype; 2 3 public interface Product extends Cloneable{ 4 public abstract void use(String word); 5 public abstract Product createClone(); 6 }
Underline实现类:
1 package zyr.dp.prototype; 2 3 public class Underline implements Product { 4 5 char ch; 6 public Underline(char ch){ 7 this.ch=ch; 8 } 9 10 public void use(String word) { 11 System.out.print(ch); 12 System.out.print(word); 13 System.out.println(ch); 14 for(int i=0;i<word.getBytes().length+1;i++){ 15 System.out.print(ch); 16 } 17 System.out.println(); 18 } 19 20 public Product createClone(){ 21 Product p=null; 22 try { 23 p = (Product)clone(); 24 } catch (CloneNotSupportedException e) { 25 e.printStackTrace(); 26 } 27 return p; 28 } 29 30 }
MessageBox实现类:
1 package zyr.dp.prototype; 2 3 public class MessageBox implements Product { 4 5 char ch; 6 public MessageBox(char ch){ 7 this.ch=ch; 8 } 9 10 public void use(String word) { 11 for(int i=0;i<word.getBytes().length+1;i++){ 12 System.out.print(ch); 13 } 14 System.out.println(); 15 16 System.out.print(ch); 17 System.out.print(word); 18 System.out.println(ch); 19 20 for(int i=0;i<word.getBytes().length+1;i++){ 21 System.out.print(ch); 22 } 23 System.out.println(); 24 } 25 26 public Product createClone(){ 27 Product p=null; 28 try { 29 p = (Product)clone(); 30 } catch (CloneNotSupportedException e) { 31 e.printStackTrace(); 32 } 33 return p; 34 } 35 36 }
Manager类:
1 package zyr.dp.prototype; 2 3 import java.util.HashMap; 4 5 public class Manager { 6 HashMap hashmap = new HashMap(); 7 public void register(String key,Product p){ 8 hashmap.put(key, p); 9 } 10 public Product create(String key){ 11 Product p=(Product)hashmap.get(key); 12 return p.createClone(); 13 } 14 15 }
运行结果:
我们将Manager做一定修改:
1 public Product create(String key){ 2 Product p=(Product)hashmap.get(key); 3 Product p1=p.createClone(); 4 System.out.println(p+":"+p.getClass()); 5 System.out.println(p1+":"+p1.getClass()); 6 7 System.out.println(p==p1); 8 System.out.println(p.getClass()==p1.getClass()); 9 10 return p1; 11 }
看一下结果,引用的地址是不同的,但是使用的类是相同的,也就是原型是相同的。并且clone()在product层次上是深复制,可是如果深入下去就会发现其实是浅复制,比如深入到char级别就能看到了,可以说使用clone()很难实现完全的深复制,基本上都是半深半浅复制,注意深复制和浅复制其实就是深拷贝和浅拷贝。
三、总结
使用原型模式,可以将一些看似相近但难以归为一类的对象进行设计,使得用户可以根据自己的需要来进行保存(register)和复制(create),我们可以设想自己的程序在进行了一系列复杂的操作之后得到了一个结果,这个结果存放在某一个对象a中,对于a我们当然可以创建new A(),可是创建出的对象是不能存储a的状态的,我们使用原型中的register将状态保存,将a存储下来,放到HashMap中,以后使用的时候直接去出来复制一份直接用,这样就非常的方便了,将中间状态存储下来并粘贴使用,同时使用接口来设计,便于不同的类的兼容(Underline和MessageBox)。我们可以看到其实原型模式就是保存(register)、复制(clone)和粘贴(clone的结果),但是我们要知道clone方法其实是在所有对象的超类(Object)中定义的,但是需要使用Cloneable接口来告诉编译器要使用这个东西,而Cloneable接口里面其实什么也没干,只是一个标签作用。并且clone()可以实现浅拷贝或者深拷贝,所谓浅拷贝,其实就是引用,搞得那么复杂完全没意义,就是A a=new A(); A b=a;这样a和b其实都指向了同一块内存空间,任何一个修改都会引起另一个取值的变换,而深拷贝是另外开辟一块空间,将原来对象的内存内容完全拷贝过来,类似于memcpy的作用,在实际应用中,基本上都是半深半浅拷贝,或者说不完全深拷贝,这点我们要非常注意,即便是我们重写了clone()依旧只是保证某个程度(级别)的深拷贝而已,除非是精细到了基本类型int,char等,对于String都需要重写clone()。
至此,我们学习了两种生成对象的方法,一种是使用单例模式,生成全局唯一对象,另一种是使用clone方式深浅拷贝对象,这两种方式在实际生活中都有着具体的用处,应该根据情况做出选择。