距離上次更新博客已通過去10天了,按計劃這篇博客早該更新了,可計劃趕不上變化因爲事情太多,致使該計劃不斷延期,這不終於有塊空閒時間了,得趕忙補上。哈哈。php
好了,言歸正傳,今天我給你們說說php中的工廠模式。架構
工廠模式可分爲簡單工廠、工廠和抽象工廠,具體區別是什麼呢?下面咱們經過實例來一步一步講解:app
首先咱們設想一個任務場景:學習
假設有個關於我的事務管理的項目,功能之一就是管理Appointment(預定)對象。咱們的業務團隊和A公司創建了關係,目前須要使用一個叫作BloggsCal格式來和他們交流預定相關的數據。可是業務部門提醒可能會有更多的數據格式。優化
任務分析:this
由於解碼器可能會有多種,爲了不在邏輯代碼中使用過多的if else,這裏須要使用工廠模式來將創造者和使用者分開,此時須要兩個類,一個類AppEncoder用於定義一個解碼器,將A公司傳來的數據解碼;另一個類CommsManager用於獲取該解碼器,用於與A公司進行通訊。使用模式術語說,CommsManager就是創造者,AppEncoder就是產品。(一個創造者、一個產品,將類的實例化和對象的使用分離開,這就是工廠模式的思想)編碼
上面的任務場景咱們能夠經過簡單工廠模式實現,具體代碼以下:spa
1 <?php 2 //產品類 3 class BloggsApptEncoder { 4 function encode() 5 { 6 return "Appointment data encoded in BloggsCal format\n"; 7 } 8 } 9 10 //創造者類 11 class CommsManager { 12 function static getBloggsApptEncoder() 13 { 14 return new BloggsApptEncoder();
15 }
16 }
使用方法比較簡單,這裏不作贅述。設計
此時又有新的需求,業務部門告訴咱們須要新增一種數據格式MegCal,來完成數據交流。此時咱們須要新增對應的解碼器類,而後直接在commsManager新增參數來標識須要實例化哪一個解碼器。代碼以下:code
<?php class CommsManager { const BLOGGS = 1; const MEGA = 2; private $mode; public function __construct( $mode ) { $this->mode = $mode; } function getApptEncoder() { switch($this->mode) { case (self::MEGA): return new MegaApptEncoder(); default: return new BloggsApptEncoder(); } } }
這即是簡單工廠模式了。那麼它帶來了什麼好處呢?
首先,符合現實中的狀況;並且客戶端免除了直接建立產品對象的責任,而僅僅負責「消費」產品(正如暴發戶所爲)。
下面咱們從開閉原則上來分析下簡單工廠模式。當新增一種數據格式的時候,只要符合抽象產品格式,那麼只要通知工廠類知道就能夠被使用了。(即建立一個新的解碼器類,繼承抽象解碼器ApptEncoder)那麼對於產品部分來講,它是符合開閉原則的——對擴展開放、對修改關閉;可是工廠類不太理想,由於每增長一各格式,都要在工廠類中增長相應的商業邏輯和判斷邏輯,這顯天然是違背開閉原則的。
而在實際應用中,極可能產品是一個多層次的樹狀結構。因爲簡單工廠模式中只有一個工廠類來對應這些產品,因此這可能會把咱們的上帝類壞了。
所以簡單工廠模式只適用於業務簡單的狀況下或者具體產品不多增長的狀況。而對於複雜的業務環境可能不太適應了。這就應該由工廠方法模式來出場了!!
業務部門的新需求來了:
每種格式的預定數據中,須要提供頁眉和頁腳來描述每次預定
擴展以前咱們用簡單工廠模式來實現上述功能,具體代碼以下:
// 簡單工廠模式
class CommsManager { const BLOGGS = 1; const MEGA = 2; private = $mode; public function __construct( $mode ) { $this->mode = $mode; } // 生成解碼器對應的頁眉 public function getHeaderText() { switch( $this->mode ) { case ( self::MEGA ): return "MegaCal header\n"; default: return "BloggsCal header\n"; } } // 生成解碼器 public function getApptEncoder() { switch( $this->mode ) { case ( self::MEGA ): return new MegaApptEncoder(); default: return new BloggsApptEncoder();; } } }
可見:此時相同的條件語句switch在不一樣的方法中出現了重複,並且若是添加新的數據格式須要改動的類過多。因此須要對咱們的結構進行修改,以求更容易擴展和維護,咱們可使用創造者子類分別生成對應的產品,這樣添加新的數據格式時,只須要添加一個創造者子類便可,方便擴展和維護。具體代碼以下:
// 工廠模式
abstract class CommsManager { abstract function getHeaderText(); abstract function getApptEncoder(); abstract function getFooterText(); } class BloggsCommsManager extends CommsManager { function getHeaderText() { return "BloggsCal Header\n"; } function getApptEncoder() { return new BloggsApptEncoder(); } function getFooterText() { return "BloggsCal Footer\n"; } } class MegaCommsManager extends CommsManager { function getHeaderText() { return "MegaCal Header\n"; } function getApptEncoder() { return new MegaApptEncoder(); } function getFooterText() { return "MegaCal Footer\n"; } }
此時,若是有新的數據格式,只須要添加一個創造類的子類便可。此時想獲取MegaCal對應的解碼器直接經過MegaCommsManager::getApptEncoder()獲取便可;當前架構已經能夠知足目前的需求,可是別得意哦,這些討厭的產品又來需求了,他們不只須要和A公司交流預定數據(Appointment),還須要交流待辦事宜(Ttd)、聯繫人(Contact)等數據。一樣的這些數據交流的格式也是BloggsCal和MegaCal.那麼咱們該如何設計呢?直接在對應解碼器的子類中添加處理事宜(TtD)和聯繫人(Contact)的方法,代碼以下:
// 抽象工廠模式
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\n"; } function getApptEncoder() { return new BloggsApptEncoder(); } function getTtdEncoder() { return new BloggsTtdEncoder(); } function getContactEncoder() { return new BloggsContactEncoder(); } function getFooterText() { return "BloggsCal Footer\n"; } } class MegaCommsManager extends CommsManager { function getHeaderText() { return "MegaCal Header\n"; } function getApptEncoder() { return new MegaApptEncoder(); } function getTtdEncoder() { return new MegaTtdEncoder(); } function getContactEncoder() { return new MegaContactEncoder(); } function getFooterText() { return "MegaCal Footer\n"; } } //固然須要添加對應的TtdEncoder抽象類和ContactEncoder抽象類,以及他們的子類。
上面就是工廠模式和及其變形,核心在於:
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_init ); abstract function getFooterText(); } class BloggsCommsManager extends CommsManager { function getHeaderText() { return "BloggsCal Header\n"; } function make( $flag_init ) { switch ($flag_init) { case self::APPT: return new BloggsApptEncoder(); case self::TTD: return new BloggsTtdEncoder(); case self::CONTACT: return new BloggsContactEncoder(); } } function getFooterText() { return "BloggsCal Header\n"; } }
若是還須要添加交流其它的數據,此時只需在抽象創造者中添加一個新的flag_init標識,並在子創造者中的make方法中添加一個條件。相比來講比原先的更加容易擴展。只需修改少數地方便可。
總結:
簡單工廠:適用於生成數量少,功能簡單的產品(BloggApptEncoder和MegaApptEncoder)
工廠模式:適用於生成數量多,功能複雜的產品(多個產品樹[BloggCal,MegaCal]、單個產品族[apptEncoder]),相比簡單工廠來講:業務更復雜,功能更多,可是產品族仍是單個。
抽象工廠:適用於生成多個產品族、多個產品樹的情景(產品族[appt,ttd,contact],產品樹[Bloggcal,megaCal])。相比於工廠模式,更容易擴展添加新的產品族。
以上就是我對php中簡單工廠、工廠模式和抽象工廠的初步理解,感謝您的閱讀
注:因本人的技術有限,若是有理解錯誤的地方,還請各位批評指正,共同交流學習,謝謝。我會繼續努力的。