【設計模式】原型模式

引子

  古人云:書非借不能讀也。 java

  如今 IT 書籍更新快、價格貴、質量水平更是良莠不齊,實在不忍心看到用本身的血汗錢買的書不到半年就要被淘汰算法

  更不想供養使用金山快譯、詞霸等現代化工具的翻譯們。數據庫

  因而去書店辦了張借書卡,這樣便沒有了後顧之憂了——書很差我能夠換嘛!緩存

  可是,借書也有不爽的地方,就是看到有用或者比較重要的地方,不能在書旁標記下來。ide

  通常會將這頁內容複印下來,這樣做爲本身的東西就能夠對其圈圈畫畫,保存下來了。函數

  在軟件設計中,每每也會遇到相似或者類似的問題,GOF 將這種解決方案叫做原型模式。工具

  也許原形模式會給你一些新的啓迪。 編碼

定義與結構

  原型模式屬於對象建立模式,GOF 給它的定義爲:用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。spa

  在 Java 中提供了 clone()方法來實現對象的克隆,因此 Prototype 模式實現變得簡單許多prototype

  使用克隆方式來建立對象與一樣用來建立對象的工廠模式有什麼不一樣?

  前面已經提過工廠模式對新產品的適應能力比較弱:建立新的產品時,就必須修改或者增長工廠角色。

  並且爲了建立產品對象要先額外的建立一個工廠對象。那經過原型模式來建立對象會是什麼樣子呢?

原型模式結構

  1. 客戶角色:讓一個原型克隆本身來獲得一個新對象。
  2. 抽象原型角色:實現了本身的 clone 方法,扮演這種角色的類一般是抽象類,且它具備許多具體的子類。
  3. 具體原型角色:被複制的對象,爲抽象原型角色的具體子類。

  先上一張類圖   

  

  按照定義客戶角色不只要負責使用對象,並且還要負責對象原型的生成和克隆。

  這樣形成客戶角色分工就不是很明確,因此咱們把對象原型生成和克隆功能單拿出來放到一個原型管理器中。

  原型管理器維護了已有原型的清單。客戶在使用時會向原型管理器發出請求,並且能夠修改原型管理器維護的清單。

  這樣客戶不須要編碼就能夠實現系統的擴展。

類圖表示

   

分析

  對於抽象原型角色和具體原型角色,它們就是一個繼承或者實現關係,沒有什麼好玩的,記住實現好 clone 方法就行了。

  那麼客戶是怎麼來使用這些角色的對象的呢?

  最簡單的方式就是:

            //先 new 一個具體原型角色做爲樣本
        Prototype p = new ConcretePrototype();
        ……
        //使用原型 p 克隆出一個新對象 p1
        Prototype p1 = (Prototype)p.clone();    
View Code

  固然這只是簡單的表述原型模式的運行過程。

  實際運用中,客戶程序與原型角色之間每每存在一個原型管理器(例子見下)。

  所以建立原型角色、拷貝原型角色就與客戶程序分離開來

  這時才能真正的體會到原型模式帶給咱們的效果。

//使用原型管理器後,客戶得到對象的方式
Prototype p1 = PrototypeManager. getManager().getPrototype(「ConcretePrototype」);
View Code

 

  上面提到的原型管理器的實現,簡單來講就是對原型清單的維護。

  能夠考慮一下幾點:

  1. 要保存一個原型對象的清單,咱們可使用一個 HashMap 來實現,使原型對象和它的名字相對應;
  2. 原型管理器只須要一個就夠了,因此可使用單例模式來實現控制;
  3. 實現獲得、註冊、刪除原型對象的功能只是對 HashMap 的對應操做而已。
 1 class PrototypeManager {
 2      private static PrototypeManager pm;
 3      private Map prototypes=null;
 4      private PrototypeManager() {
 5           prototypes=new HashMap();
 6      }
 7 
 8 //使用單例模式來獲得原型管理器的惟一實例
 9 public static PrototypeManager getManager() {
10     if(pm==null) {
11           pm=new PrototypeManager();
12     }
13     return pm;
14 }
15 public void register(String name , Object prototype) {
16     prototypes.put(name , prototype);
17 }
18 public void unregister(String name) {
19     prototypes.remove(name);
20 }
21 public Prototype getPrototype(String name) {
22     if(prototypes.containsKey(name)) {
23           //將清單中對應原型的複製品返回給客戶
24      return (Prototype) ((Prototype)prototypes.get(name)).clone();
25 }else {
26      Prototype object=null;
27      try {
28            object =(Prototype)Class.forName(name).newInstance();
29     register(name , object);
30 } catch(Exception e) {
31     System.err.println("Class "+name+"沒有定義!");
32 }
33 return object;
34 。。。
View Code

  這樣當客戶自定義新的產品對象時,同時向原型管理器註冊一個原型對象,而使用的類只須要根據客戶的須要來從原型管理器中獲得一個對象就能夠了。

  這樣就使得功能擴展變得容易些。

  原型模式與其它建立型模式有着相同的特色:它們都將具體產品的建立過程進行包裝,使得客戶對建立不可知。

  就像上面例子中同樣,客戶程序僅僅知道一個抽象產品的接口。

  固然它還有過人之處

  1. 經過增長或者刪除原型管理器中註冊的對象,能夠比其它建立型模式更方便的在運行時增長或者刪除產品。
  2. 若是一個對象的建立老是由幾種固定組件不一樣方式組合而成;
  3. 若是對象之間僅僅實例屬性不一樣。
  4. 將不一樣狀況的對象緩存起來,直接克隆使用。
  5. 也許這比採用傳遞參數從新 new 一個對象要來的快一些。

  你也許已經發現原型模式與工廠模式有着千絲萬縷的聯繫

  1. 原型管理器不就是一個工廠麼。
  2. 固然這個工廠通過了改進(例如上例採用了 java 的反射機制),去掉了像抽象工廠模式或者工廠方法模式那樣繁多的子類。
  3. 所以能夠說原型模式就是在工廠模式的基礎上加入了克隆方法。

  也許你要說:我實在看不出來使用 clone 方法產生對象和 new 一個對象有什麼區別

  1. 原型模式使用 clone 可以動態的抽取當前對象運行時的狀態而且克隆到新的對象中,新對象就能夠在此基礎上進行操做而不損壞原有對象; 
  2. new 只能獲得一個剛初始化的對象,而在實際應用中,這每每是不夠的。
  3. 特別當你的系統須要良好的擴展性時,在設計中使用原型模式也是很必要的。
  4. 好比說,你的系統可讓客戶自定義本身須要的類別,可是這種類別的初始化可能須要傳遞多於已有類別的參數,而這使得用它的類將不知道怎麼來初始化它(由於已經寫掛了),除非對類進行修改。
  5. 可見 clone 方法是不能使用構造函數來代替的。

  分析了這麼多了,舉一個使用原型模式較爲經典的例子

  績效考覈軟件要對今年的各類考覈數據進行年度分析,而這一組數據是存放在數據庫中的。

  通常咱們會將這一組數據封裝在一個類中,而後將此類的一個實例做爲參數傳入分析算法中進行分析,獲得的分析結果返回到類中相應的變量中。

  假設咱們決定對這組數據還要作另一種分析以對分析結果進行比較評定。

  這時對封裝有這組數據的類進行 clone 要比再次鏈接數據庫獲得數據好的多。

  任何模式都是存在缺陷的。

  原型模式主要的缺陷就是每一個原型必須含有 clone 方法,在已有類的基礎上來添加 clone 操做是比較困難的;

  並且當內部包括一些不支持 copy 或者循環引用的對象時,實現就更加困難了。

總結

  因爲 clone 方法在 java 實現中有着必定的弊端和風險,因此 clone 方法是不建議使用的。

  所以不多能在 java 應用中看到原型模式的使用。

  可是原型模式仍是可以給咱們一些啓迪。

 

  @成鵬致遠

(blogs:lcw.cnblogs.com

(emailwwwlllll@126.com)

(qq552158509)

相關文章
相關標籤/搜索