原型模式(Prototype Pattern)

  1. 參考書籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》
  2. 設計模式包教不包會

很多講解原型模式的博文,甚至視屏教程中都把原型模式最主要的目的列爲效率的提升, 美其名曰複製一個對象比創建一個對象開銷更小,並由此引伸到“深拷貝”“淺拷貝”的知識點。 很顯然, 這種說法差強人意, 並非原型模式的設計由來。

設計模式用前須知

  • 設計模式中一句出現頻率非常高的話是,“ 在不改動。。。。的情況下, 實現。。。。的擴展“ 。
  • 對於設計模式的學習者來說,充分思考這句話其實非常重要, 因爲這句往往只對框架/工具包的設計纔有真正的意義。因爲框架和工具包存在的意義,就是爲了讓其他的程序員予以利用,進行功能的擴展,而這種功能的擴展必須以不需要改動框架和工具包中代碼爲前提
  • 對於應用程序的編寫者, 從理論上來說,所有的應用層級代碼至少都是處於可編輯範圍內的, 如果不細加考量, 就盲目使用較爲複雜的設計模式, 反而會得不償失, 畢竟靈活性的獲得,也是有代價的

原型模式(Prototype)

  • 設計意圖

    • 利用原型實例指定要創建的對象類型,通過拷貝該原型來創建新的實例。
  • 舉例:

    • 有一個用於編寫圖形編輯器的通用框架, 我們用它來編寫一個樂譜編輯器。 具體的做法是, 我們要在原有的框架基礎上, 擴展定義一些新的基本圖形元素(Graphic)對象, 例如音符, 休止符, 五線譜等元素。

    • 用戶在使用該樂譜編輯器時, 最常見的操作可能是【添加操作】: 點擊一個四分音符**,然後將其拖動到文檔中, 在相應的位置再點擊一下,完成音符的添加。** (這裏可以聯想一下PhotoShop 或 Microsoft Viso 的操作界面) 。

    • 樂譜

    • 現在假設框架提供了一個Graphic 抽象類作爲圖形組件(音符、五線譜)的基類,同時也提供了抽象工具類Tool 作爲各種圖形操作工具的基類。 在此基礎上, 實現了子類GraphicTool 用於創建各種圖形組件對象的實例, 將其添加到文檔中的操作。

    • 此時, 很自然的, 要利用這個框架編寫樂譜編輯器, 我們會首先想定義一些樂譜元素的類,如四分音符(QuaterNote), 二分音符(HalfNote), 一分音符(OneNote), 休止符(Rest) , 這些類繼承自Graphic 對象, 然後我們就可以擴展GraphicTool 類, 實現用於添加樂譜元素的工具。

    • 注意: 這裏便對框架設計者提出了一個問題, 音符類和休止符類都是應用程序獨有的類, 但是GraphicTool 是要包含在框架中的一個類, GraphicTool 並不知道怎麼樣去new 用戶自定義的音符類。 當然, 框架設計者可以讓用戶針對不同的音符類去擴展GraphicTool , 實現QuaterNoteGraphicTool, HalfNoteGraphicTool, OneNoteGraphicTool 等, 但是這樣會產生大量的子類, 而他們的區別僅僅是要創建的實例對象不同。 所以現在我們可能更希望可以僅僅將自定義的類QuarterNote作爲一個參數傳遞給GraphicTool, GraphicTool就能完成相應的創建操作。
      引自參考書籍1

  • 圖例說明

    • 圖片中的空心三角箭頭,代表着繼承(extends)或實現(Implement)關係, 由繼承者/實現者 指向 被繼承者/被繼承者。
    • 圖片中的實心三角箭頭且箭頭末尾沒有圓圈的, 代表着單一的引用關係, 但是被引用的對象也有可能被其他對象引用。
    • 圖片中的實心三角箭頭且箭頭末尾有圓圈的, 代表着一對多的引用關係。
    • 圖片中的虛線實心三角箭頭, 代表着創建或者實例化的關係。
    • 圖片中的末端有圓圈的虛線是一個對方法體內容用僞代碼說明的關係
  • 這個地方聽起來有點像簡單工廠套路(注意簡單工廠不是GOF 23種設計模式之一), 但是稍加思考就會發現並非如此, 因爲如果應用簡單工廠套路 , 那麼GrphicTool 就需要持有一個工廠類負責不同對象的創建, 但是這個工廠類也許要包含在框架中, 很顯然, 用戶不能去修改這個工廠類中的內容去返回不同的對象,完成擴展。

  • 如果不像簡單工廠套路, 那是否和工廠方法模式類似呢? 答案是不。 因爲工廠方法模式的核心是將父類中的實例化操作延遲到子類進行, 這裏的場景並不符合, 因爲我們並不希望繼承GrphicTool 去實現新的子類, 而是直接利用GraphicTool 創建不同的對象。

  • 那這裏是否和抽象工廠模式類似呢?的確如此, 因爲GrphicTool 可以持有一個抽象工廠對象AbstractGrphicFactory, 然後,我們可以向其傳入不同的,由用戶擴展實現子類(例如QuaterNoteGrphicFactory) , 完成不同對象(如QuaterNote)的實例化操作。那麼抽象工廠和原型模式的區別是什麼?

原型模式與抽象工廠模式的區別

  • 原型模式

    • 上文提到的樂譜編輯器的例子中, 原型模式的核心在於, GraphaicTool 創建一個新的Grphic 對象的方式, 是通過複製(Copying)或者克隆(Clone) 一個Graphic 子類的實例實現的。我們管這個實例叫做原型。 GrphicTool 被傳入了“原型“參數(可以是在構造時傳入,也可以是通過set 傳入, 也可以是在調用方法時傳入), 它可以利用這個原型來創建新的對象。 通過這種方式, 圖形編輯器框架, 就可以實現一個GrphaicTool, 創建任何用戶擴展於 Graphic 的對象(這裏回想一下設計模式用前須知就可以理解原型模式的此處的必要性
  • 抽象工廠方法模式

    • 上述提到的問題,同樣可以用抽象工廠模式來解決,GrphicTool 可以持有一個抽象工廠對象AbstractGrphicFactory, 然後,我們可以向其傳入不同的,由用戶擴展實現子類(例如QuaterNoteGrphicFactory) , 完成不同對象(如QuaterNote)的實例化操作。
    • 但是, 這樣造成的結果是, 每擴展一個新的圖形組件Grphic ,就需要專門再創建一個相對應的工廠, 只生產這一種產品。 這樣不僅僅會產生大量的子類,還違背了抽象工廠模式設計的初衷: 解決一系列相互依賴的產品創建, 且保證不同族的產品不被混用。

原型模式總結

引自參考書籍11

  • 原型模式適用場景:
    • 有一個通用的方法中需要創建一個實例。
    • 這個實例, 有可能被用戶擴展出很多不同的類型。
    • 對於不同的擴展出的類型,都希望通用的方法可以被繼續使用。
發佈了51 篇原創文章 · 獲贊 264 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章