一、定义
在应用程序中,有些对象比较复杂,其创建过程过于复杂,而且我们又需要频繁的利用该对象,如果这个时候我们按照常规思维new该对象,那么务必会造成资源浪费,这个时候我们就希望可以利用一个已有的对象来不断对他进行复制就好了,这就是编程中的“克隆”。原型模式直接操作底层二进制流,在创建复杂对象是效率提升明显。
浅克隆与深克隆:
- 浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
- 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。
原型模式:是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,
工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
二、角色
- 带Prototype Manager的原型模式:客户(Client)角色:客户端类向原型管理器提出创建对象的请求。
- 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
- 具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
- 原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
类图如下:
三、场景展示
我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。
首先定义实现了 Cloneable 接口的抽象类
package com.ssy.wlj.prototype; /** * 类说明:实现了 Cloneable 接口的抽象类 * * @author happy * @since 2019年5月25日 * */ 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; } }
创建抽象类的实体类
package com.ssy.wlj.prototype; /** * 类说明: 正方形 * @author happy * @since 2019年5月25日 * */ public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } }
package com.ssy.wlj.prototype; /** * 类说明:长方形 * * @author happy * @since 2019年5月25日 * */ public class Rectangle extends Shape { public Rectangle() { type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
package com.ssy.wlj.prototype; /** * 类说明: 圆 * @author happy * @since 2019年5月25日 * */ public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }
创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。
package com.ssy.wlj.prototype; import java.util.Hashtable; /** * 类说明: * * @author happy * @since 2019年5月25日 * */ 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); } }
创建客户端类,使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
package com.ssy.wlj.prototype; /** * 类说明: * * @author happy * @since 2019年5月25日 * */ public class Client { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = (Shape) ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }
优点:
- 当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程。
- 直接操作二进制流,可以提高实例的创建效率。
缺点:
- 需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
- 此外clone对象时,不调用构造方法,无视构造方法的权限。