原型模式

原型模式(Prototype Pattern)用一个已经创建的范例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。原型模式用于创建重复的对象,同时又能保证性能。原型模式属于创建型模式,它提供了一种创建对象的最佳方式。

原型模式实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则可以采用这种模式。

介绍

意图:用原型范例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

主要解决:在运行期建立和删除原型。

何时使用:

  • 1、当一个系统应该独立于它的产品创建,构成和表示时。
  • 2、当要范例化的类是在运行时刻指定时,例如,通过动态装载。
  • 3、为了避免创建一个与产品类层次平行的工厂类层次时。
  • 4、当一个类的范例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工范例化该类更方便一些。

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的范例。

关键代码:

  • 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。
  • 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

应用范例: JAVA 中的 Object clone() 方法。

优点:

  • 1、Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 2、可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

缺点:

  • 1、必须实现 Cloneable 接口,配置一个 clone 方法。当对已有类进行改造的时候,需要修改 clone 代码,违背了开闭原则。
  • 2、当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
    • 使用场景:

      • 1、对象之间相同或相似,即只是个别的几个属性不同的时候。
      • 2、创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
      • 3、创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
      • 4、系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

      注意事项:与通过对一个类进行范例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

      实现

      我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。

      PrototypePatternDemo 类使用 ShapeCache 类来获取 Shape 对象。

      原型模式的 UML 图

      步骤 1

      创建一个实现了 Cloneable 接口的抽象类。

      Shape.java

      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; } }

      步骤 2

      创建扩展了上面抽象类的实体类。

      Rectangle.java

      public class Rectangle extends Shape { public Rectangle(){ type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }

      Square.java

      public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } }

      Circle.java

      public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }

      步骤 3

      创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。

      ShapeCache.java

      import java.util.Hashtable; 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); } }

      步骤 4

      PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。

      PrototypePatternDemo.java

      public class PrototypePatternDemo { 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()); } }

      步骤 5

      执行程序,输出结果:

      Shape : Circle
      Shape : Square
      Shape : Rectangle
      

      将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式属于结构型模式。适配器模式作为两个不兼容的接口之间的桥梁,它结合了两个独立接口的功能