软件设计模式——原型模式(Prototype)
2016-04-19 16:43
176 查看
转载自:http://my.csdn.net/wangyanguiyiyang
Java 中提供了clone()方法,使用Object的clone()方法来实现对象的克隆,所以Prototype 模式实现变得简单许多。
既然用到clone()方法,不可避免的我们就会涉及到深拷贝和浅拷贝。
浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用指向类型,指向的还是原对象所指向的。
深拷贝:将一个对象复制后,不论是基本数据类型还有引用都重新创建的。简单来说,深拷贝进行了完全彻底的复制,而浅拷贝复制不彻底。
使用克隆方式来创建对象与同样用来创建对象的工厂模式有什么不同?前面已经提过工厂模式对新产品的适应能力比较弱:创建新的产品时,就必须修改或者增加工厂角色。而且为了创建产品对象要先额外的创建一个工厂对象。那通过原型模式来创建对象会是什么样子呢?
先让我们来看看原型模式的结构吧。
1) 客户角色(Client):让一个原型克隆自己来得到一个新对象。
2) 抽象原型角色(Prototype):实现了自己的clone 方法,扮演这种角色的类通常是抽象类,且它具有许多具体的子类。
3) 具体原型角色(ConcretePrototype):被复制的对象,为抽象原型角色的具体子类。
类图:
按照定义客户角色不仅要负责使用对象,而且还要负责对象原型的生成和克隆。这样造成客户角色分工就不是很明确,所以我们把对象原型生成和克隆功能单拿出来放到一个原型管理器中。原型管理器维护了已有原型的清单。客户在使用时会向原型管理器发出请求,而且可以修改原型管理器维护的清单。这样客户不需要编码就可以实现系统的扩展。
类图表示如下:
PrototypPatternDemo,我们的演示类使用 ShapeCache 类来获取 Shape 对象。
运行结果:
Shape:Circle
Shape:Square
Shape:Square
如果一个对象的创建总是由几种固定组件不同方式组合而成;如果对象之间仅仅实例属性不同。将不同情况的对象缓存起来,直接克隆使用。也许这比采用传递参数重新new 一个对象要来的快一些。
你也许已经发现原型模式与工厂模式有着千丝万缕的联系:原型管理器不就是一个工厂么。当然这个工厂经过了改进(例如上例采用了java 的反射机制),去掉了像抽象工厂模式或者工厂方法模式那样繁多的子类。因此可以说原型模式就是在工厂模式的基础上加入了克隆方法。
也许你要说:我实在看不出来使用 clone 方法产生对象和new 一个对象有什么区别;
原型模式使用clone 能够动态的抽取当前对象运行时的状态并且克隆到新的对象中,新对象就可以在此基础上进行操作而不损坏原有对象;而new 只能得到一个刚初始化的对象,而在实际应用中,这往往是不够的。
特别当你的系统需要良好的扩展性时,在设计中使用原型模式也是很必要的。比如说,你的系统可以让客户自定义自己需要的类别,但是这种类别的初始化可能需要传递多于已有类别的参数,而这使得用它的类将不知道怎么来初始化它(因为已经写死了),除非对类进行修改。可见 clone 方法是不能使用构造函数来代替的。
定义与结构
原型模式属于对象创建模式,我们从名字即可看出该原型模式的思想就是将一个对象作为原型,其进行复制、克隆产生和类似的新对象。GOF给它的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。Java 中提供了clone()方法,使用Object的clone()方法来实现对象的克隆,所以Prototype 模式实现变得简单许多。
既然用到clone()方法,不可避免的我们就会涉及到深拷贝和浅拷贝。
浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用指向类型,指向的还是原对象所指向的。
深拷贝:将一个对象复制后,不论是基本数据类型还有引用都重新创建的。简单来说,深拷贝进行了完全彻底的复制,而浅拷贝复制不彻底。
使用克隆方式来创建对象与同样用来创建对象的工厂模式有什么不同?前面已经提过工厂模式对新产品的适应能力比较弱:创建新的产品时,就必须修改或者增加工厂角色。而且为了创建产品对象要先额外的创建一个工厂对象。那通过原型模式来创建对象会是什么样子呢?
先让我们来看看原型模式的结构吧。
1) 客户角色(Client):让一个原型克隆自己来得到一个新对象。
2) 抽象原型角色(Prototype):实现了自己的clone 方法,扮演这种角色的类通常是抽象类,且它具有许多具体的子类。
3) 具体原型角色(ConcretePrototype):被复制的对象,为抽象原型角色的具体子类。
类图:
按照定义客户角色不仅要负责使用对象,而且还要负责对象原型的生成和克隆。这样造成客户角色分工就不是很明确,所以我们把对象原型生成和克隆功能单拿出来放到一个原型管理器中。原型管理器维护了已有原型的清单。客户在使用时会向原型管理器发出请求,而且可以修改原型管理器维护的清单。这样客户不需要编码就可以实现系统的扩展。
类图表示如下:
原型模式的实现
我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。PrototypPatternDemo,我们的演示类使用 ShapeCache 类来获取 Shape 对象。
//抽象原型角色--Prototype public abstract class Shape implements Cloneable{ private String id; protected String type; abstract void draw(); public String getType() { return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone(){ Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
//具体原型角色--ConcretePrototype1 public class Rectangle extends Shape { public Rectangle() { type = "Rectangle"; } @Override void draw() { System.out.println("Inside Rectangle::draw() method"); } }
//具体原型角色--ConcretePrototype2 public class Square extends Shape{ public Square() { type = "Square"; } @Override void draw() { System.out.println("Inside Square::draw() method."); } }
//具体原型角色--ConcretePrototype3 public class Circle extends Shape{ public Circle() { type = "Circle"; } @Override void draw() { System.out.println("Inside Circle::draw() method."); } }
//原型管理器--PrototypeManager public class ShapeCache { private static Hashtable<String, Shape>shapeMap = new Hashtable<String,Shape>(); public static Shape getShape(String shapeId){ Shape cachedShape = shapeMap.get(shapeId); return (Shape)cachedShape.clone(); } // 对每种形状都运行数据库查询,并创建该形状 // shapeMap.put(shapeKey, shape); // 例如,我们要添加三种形状 public static void loadCache(){ Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(), circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(), square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(), rectangle); } }
//客户角色--Client public class Client { public static void main(String[] args) { ShapeCache.loadCache(); Shape cloneShape = (Shape)ShapeCache.getShape("1"); System.out.println("Shape:"+cloneShape.getType()); Shape clonedShape2 = (Shape)ShapeCache.getShape("2"); System.out.println("Shape:"+clonedShape2.getType()); Shape clonedShape3 = (Shape)ShapeCache.getShape("3"); System.out.println("Shape:"+clonedShape2.getType()); } }
运行结果:
Shape:Circle
Shape:Square
Shape:Square
原型模式的分析
原型模式与其它创建型模式有着相同的特点:它们都将具体产品的创建过程进行包装,使得客户对创建不可知。如果一个对象的创建总是由几种固定组件不同方式组合而成;如果对象之间仅仅实例属性不同。将不同情况的对象缓存起来,直接克隆使用。也许这比采用传递参数重新new 一个对象要来的快一些。
你也许已经发现原型模式与工厂模式有着千丝万缕的联系:原型管理器不就是一个工厂么。当然这个工厂经过了改进(例如上例采用了java 的反射机制),去掉了像抽象工厂模式或者工厂方法模式那样繁多的子类。因此可以说原型模式就是在工厂模式的基础上加入了克隆方法。
也许你要说:我实在看不出来使用 clone 方法产生对象和new 一个对象有什么区别;
原型模式使用clone 能够动态的抽取当前对象运行时的状态并且克隆到新的对象中,新对象就可以在此基础上进行操作而不损坏原有对象;而new 只能得到一个刚初始化的对象,而在实际应用中,这往往是不够的。
特别当你的系统需要良好的扩展性时,在设计中使用原型模式也是很必要的。比如说,你的系统可以让客户自定义自己需要的类别,但是这种类别的初始化可能需要传递多于已有类别的参数,而这使得用它的类将不知道怎么来初始化它(因为已经写死了),除非对类进行修改。可见 clone 方法是不能使用构造函数来代替的。
总结
任何模式都是存在缺陷的。原型模式主要的缺陷就是每个原型必须含有clone 方法,在已有类的基础上来添加clone 操作是比较困难的;而且当内部包括一些不支持copy 或者循环引用的对象时,实现就更加困难了。相关文章推荐
- Android DataBinding
- solr词库实时更新维护
- 利用Retrofit执行Post、Get请求,通俗易懂
- Android窗口类型有关
- Android 驱动开发系列三
- 常用资源分享
- OpenCV改变图像大小的操作
- ListView关于Item高度的诡异问题
- java生成和调用webservice
- 100. Same Tree
- Cartesian k-means论文理解
- CSS中越界问题的经典解决方案【推荐】
- jquery每日一学
- C#中两个Form之间传值的一种方法
- ArrayList学习笔记
- phantomjs 解码url
- C++新特性学习笔记
- 配置Log4j(很详细)
- android修炼大法
- 多线程学习08-GCD概念