原型模式


原型模式(Prototype Pattern)是用來建立重複的對象,同時又能確保效能。這種類型的設計模式屬於創建型模式,它提供了一種創建物件的最佳方式。

這種模式是實作了一個原型接口,該接口用於創建當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。例如,一個物件需要在一個高代價的資料庫操作之後被建立。我們可以快取該對象,在下一個請求時返回它的克隆,在需要的時候更新資料庫,以此來減少資料庫呼叫。

介紹

意圖:用原型實例指定建立物件的種類,並且透過拷貝這些原型建立新的物件。

主要解決:在運行期建立和刪除原型。

何時使用:1、當一個系統應該獨立於它的產品創建,構成和表示。 2.當要實例化的類別是在運行時刻指定時,例如,透過動態裝載。 3.為了避免創建一個與產品類別層次平行的工廠類別層次時。 4.當一個類別的實例只能有幾個不同狀態組合中的一種。建立相應數目的原型並克隆它們可能比每次用合適的狀態手動實例化該類別更方便一些。

如何解決:利用現有的一個原型對象,快速地產生和原型對像一樣的實例。

關鍵程式碼:1、實作複製操作,在JAVA 繼承Cloneable,重寫clone(),在.NET 中可以使用Object 類別的MemberwiseClone() 方法來實現物件的淺拷貝或透過序列化的方式來實現深拷貝。 2.原型模式同樣用於隔離類別物件的使用者和具體類型(易變類別)之間的耦合關係,它同樣要求這些"易變類別"擁有穩定的介面。

應用實例:1、細胞分裂。 2、JAVA 中的 Object clone() 方法。

優點:1、效能提升。 2、逃避構造函數的約束。

缺點:1、配備複製方法需要對類別的功能進行通盤考慮,這對於全新的類別不是很難,但對於已有的類別不一定很容易,特別當一個類別引用不支援串行化的間接對象,或引用含有循環結構的時候。 2、必須實作 Cloneable 介面。 3.逃避構造函數的約束。

使用場景:1、資源最佳化場景。 2.類別初始化需要消化非常多的資源,這個資源包括資料、硬體資源等。 3、效能和安全要求的場景。 4.透過 new 產生一個物件需要非常繁瑣的資料準備或存取權限,則可以使用原型模式。 5、一個物件多個修改者的場景。 6、一個物件需要提供給其他物件訪問,而且各個呼叫者可能都需要修改其值時,可以考慮使用原型模式拷貝多個物件供呼叫者使用。 7.在實際專案中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,透過 clone 的方法創建一個對象,然後由工廠方法提供給呼叫者。原型模式已經與 Java 融為一體,大家可以隨手拿來使用。

注意事項:與透過對一個類別進行實例化來建構新物件不同的是,原型模式是透過拷貝一個現有物件產生新物件的。淺拷貝實作 Cloneable,重寫,深拷貝是透過實作 Serializable 讀取二進位流。

實作

我們將建立一個抽象類別 Shape 和擴充了 Shape 類別的實體類別。下一步是定義類別 ShapeCache,該類別把 shape 物件儲存在一個 Hashtable 中,並在請求的時候傳回它們的克隆。

PrototypPatternDemo,我們的示範類別使用 ShapeCache 類別來取得 Shape 物件。

原型模式的 UML 图

步驟 1

建立一個實作了 Clonable 介面的抽象類別。

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