摘要:上章講的是創建型的設計模式,工廠方法上,這次要講的是另一本書關于工廠方法的一些概念以及案例模型等等。工廠方法模式假設你有一個關于個人事務管理的項目,功能之一是管理預約對象。
上章講的是創建型的設計模式,工廠方法(上),這次要講的是另一本書關于工廠方法的一些概念以及案例、模型等等。就像電影“風雨哈佛路”中那個老師提問,為什么要用另外的一張一張紙質資料,而不直接用書籍。女主回答說,因為不同的資料匯集了不同人的思想。
工廠方法模式假設你有一個關于個人事務管理的項目,功能之一是管理預約對象(Appointment)。現在要和另一個公司建立關系,需要一個叫做BloggsCal的格式來和他們交流預約相關的數據。但是你將來可能要面對更多的數據格式
在接口上可以立即定義兩個類,
1.Class ApptEncoder:數據編碼器,將Appointment轉換成一個專有格式
2.Class CommsManager:管理員類,用來獲取數據編碼器,并使用編碼器進行第三方通信
使用模型屬于來描述的話,CommsManager就是創建者(Creator),而ApptEncoder是產品(product)
那么如何得到一個具體的ApptEncoder對象?
CommsManager類負責生成BloggsApptEncoder對象,但是當你和合作方關系改變,被要求轉換系統來使用一個新的格式MegaCal時,那么代碼就需要做另外的改變了
class CommsManager{ const BLOGGS = 1; const MEGA = 2; private $mode =1; function __construct($mode){ $this->mode = $mode; } function getApptEncoder(){ switch($this->mode){ case (self::MEGA): return new MegaApptEncoder(); default: return new BloggsApptEncoder(); } } } $comms = new CommsManager(CommsManager::MEGA); $appt = $comms->getApptEncoder(); print $appt->encode();
在類中我們使用常量標志定義了腳本可能運行的兩個模式:MEGA和BLOGGS,在getApptEncoder()方法中使用switch語句來檢查$mode屬性,并實例化相關編碼器
但是這種方法還有一種小缺陷,通常情況下,創建對象需要指定條件,但是有時候條件語句會被當作Awful的“Code taste”,因為可能會導致重復的條件語句蔓延在代碼中。我們知道創建者已經能夠提供交流日歷數據的功能,但是如果合作方要求提供頁眉和頁腳來約束每次預約,那該怎么辦?
結果是,你需要在上面的代碼中加入新的方法
function getHeaderText(){ switch($this->mode){ case (self::MEGA): return "This is Mega format header! "; default: return "This is Bloggs format header! "; } }
Obviously,這會使得它在getApptEncoder()方法同時使用時,重復地使用了switch判斷,一旦客戶要增加其它需求,那工作量以及冗余程度會更重
總結一下當前需要思考的:
1.在代碼運行時我們才知道要生成的對象類型(BloggsApptEncoder或者是MegaApptEncoder)
2.我們需要能夠相對輕松地加入一些新的產品類型(如新的業務處理方式SyncML)
3.每一個產品類型都可定制特定的功能(如上文提到的頁眉頁腳)
另外注意我們使用的條件語句,其實可以被多態替代,而工廠方法模式恰好能讓我們用繼承和多態來封裝具體產品的創建,黃菊花說,我們要為每種協議創建CommsManager的每一個子類,而每一個子類都要實現getApptEncoder方法
實現工廠方法模式把創建者類與要生產的產品分離開來。創建者是一個工廠類,其中定義了用于生成產品對象的類方法,如果沒有提供默認實現,那么就由創建者類的子類來執行實例化。一般來說,就是創建者類的每個子類實例化一個相應產品子類
所以我們把CommsManager重新指定為抽象類,這樣就可以得到一個靈活的父類,并把所有特定協議相關的代碼放到具體的子類中
下面是簡化過的代碼:
abstract class ApptEncoder{ abstract function encode(); } class BloggsApptEncoder extends ApptEncoder{ function encode(){ return "Appointment data encode in BloggsCal format! "; } } abstract class CommsManager{ abstract class getHeaderText(); abstract class getApptEncoder(); abstract class getFooterText(); } class BloggsCommsManager extends CommsManager{ function getHeaderText(){ return "BloggsCal Header"; } function getHeaderText(){ return new BloggsApptEncoder(); } function getFooterText(){ return "BloggsCal Footer"; } }
現在當我們要求實現MegaCal時,只需要給CommsManager抽象類寫一個新的實現
注意到上面的創建者類與產品的層次結構很相似,這是使用工廠方法模式的常見結果,形成了一種特殊的代碼重復。另一個問題是該模式可能會導致不必要的子類化,如果你為創建者創建子類的原因是為了實現工廠方法模式,那么最好再考慮一下(這就是為什么在例子中引入頁眉頁腳)
抽象工廠模式上面例子中我們只關注了預約功能。
我們通過加入更多編碼格式,使結構“橫向”增長
如果想擴展功能,使其能夠處理待辦事宜和聯系人,那應該讓它進行縱向增長
CommsManager抽象類定義了用于生成3個產品(ApptEncoder、TtdEncoder、ContactEncoder)的接口,我們需要先實現一個具體的創建者,然后才能創建一個特定類型的具體產品,下圖模型創建了BloggsCal格式的創建
下面是CommsManager和BloggsCommsManager的代碼
abstract class CommsManager{ abstract function getHeaderText(); abstract function getApptEncoder(); abstract function getTtdEncoder(); abstract function getContactEncoder(); abstract function getFooterText(); } class BloggsCommsManager extends CommsManager{ function getHeaderText(){ return "BloggsCal header "; } function getApptEncoder(){ return new BloggsApptEncoder(); } function getTtdEncoder(){ return new BloggsTtdEncoder(); } function getContactEncoder(){ return new BloggsContactEncoder(); } function getFooterText(){ return "BloggsCal footer "; } }
在這個例子中使用了工廠方法模式,getContactEncoder()是CommsManager的抽象方法,并在BloggsCommManager中實現。設計模式間經常會這樣寫作:一個模式創建可以把它自己引入到另一個模式的上下文環境中,我們加入了對MegaCal格式的支持
這樣的模式帶來了什么?
1.系統與實現的細節分離開來,我們可以在實例中添加移除任意樹木的編碼格式而不會影響系統
2.對系統中功能相關的元素強制進行組合,因此通過使用BloggsCommsManager,可以確保值使用與BloggsCal相關的類
3.添加新產品比較麻煩,不僅要創建新產品的具體實現,而且必須修改抽象創建者和它的每一個具體實現
我們可以創建一個使用標志來決定返回什么對象的單一make()方法,而不用給每個工廠方法創建獨立的方法,如下
abstract class CommsManager{ const APPT = 1; const TTD = 2; const CONTACT = 3; abstract function getHeaderText(); abstract function make($flag_int); abstract function getFooterText(); } class BloggsCommsManager extends CommsManager{ function getHeaderText(){ return "BloggsCal header"; } function make($flag_int){ switch($flag_int){ case self::APPT: return new BloggsApptEncoder(); case self::CONTACT: return new BloggsContactEncoder(); case self::TTD: return new BloggsTtdEncoder(); } } function getFooterText(){ return "BloggsCal footer "; } }
類的接口更加緊湊,但也有代價,在使用工廠方法時,我們定義了一個清晰的接口強制所有具體工廠對象遵循它,而使用丹儀的make()方法,我們必須在所有的具體創建者中支持所有的產品對象。每個具體創建者都必須實現相同的標志檢測(flag),客戶類無法確定具體的創建者是否可以生成所有產品,因為make方法需要對每種情況進行考慮并進行選擇
本章參考《深入PHP:面向對象、模式與實踐》第9章
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21259.html
摘要:利用工廠方法模式,請求者發出請求,而不具體創建產品。正是因為這個原因,使用工廠方法模式可以簡化復雜的創建過程,關鍵就在于它在維持一個公共接口。 創建型設計模式 包括以下五種: 抽象工廠 生成器 工廠方法 原型 單例 我們選擇工廠方法和原型模式作為將用PHP實現的創建型設計的例子工廠方法模式是這5個設計模式中唯一的一種類設計模式原型模式屬于對象類模式,可以使用PHP_clone方法實...
摘要:設計模式設計模式基本原則設計原則按接口而不是按實現來編程按接口而不是按實現編程是指,要將變量設置為一個抽象類或接口數據類型的實例,而不是一個具體實現的實例。例如父類的一個改變會逐級向下傳遞給子類實現,這可能會影響子類使用的某個算法。 設計模式 設計模式基本原則 設計原則 ① : 按接口而不是按實現來編程 按接口而不是按實現編程是指,要將變量設置為一個抽象類或接口數據類型的實例,而不是一...
摘要:又稱為多態性工廠模式或虛擬構造子模式。簡單工廠模式簡單工廠模式簡單工廠模式又稱為靜態工廠方法模式,它屬于類創建型模式。多態性設計工廠方法模式之所以又被稱為多態工廠模式,是因為所有的具體工廠類都具有同一抽象父類。 點擊進入我的博客 2.1 簡單工廠模式 2.1.1 工廠模式的幾種形態 工廠模式主要用一下幾種形態: 簡單工廠(Simple Factory):專門定義一個類來負責創建其他...
摘要:維基百科在軟件工程中,創建型設計模式是用于解決對象創建機制,嘗試在指定場景下使用合理的方式來創建對象的設計模式。維基百科說建造者模式是一種對象創建軟件設計模式,其目的是找到一種解決方案,以解決可伸縮構造函數的反模式。 1.創建型設計模式2.結構型設計模式3.行為型設計模式 創建型設計模式 簡而言之 創建型設計模式關注的是如何實例化一個或者一組相關的對象。 維基百科 在軟件工程中,創建型...
摘要:創建型模式主要有以下五種簡單工廠模式和工廠方法模式抽象工廠模式單例模式建造者模式原型模式在設計模式一書中將工廠模式分為兩類工廠方法模式與抽象工廠模式。 一、 設計模式(Design pattern)是什么 設計模式是一套被反復使用、多數人知曉、經過分類編目的代碼設計的經驗總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 二、 為什么會有設計模式 在軟件開發過...
閱讀 3684·2021-11-25 09:43
閱讀 2600·2021-11-18 13:11
閱讀 2194·2019-08-30 15:55
閱讀 3271·2019-08-26 11:58
閱讀 2823·2019-08-26 10:47
閱讀 2230·2019-08-26 10:20
閱讀 1271·2019-08-23 17:59
閱讀 2999·2019-08-23 15:54