另外五个 PHP 设计模式
2019獨角獸企業重金招聘Python工程師標準>>>
設計模式?一書介紹了很多此類概念。當時,我還在學習面向對象 (OO),因此我發現那本書中有許多概念都很難領會。但是,隨著越來越熟悉 OO 概念 —— 尤其是接口和繼承的使用 —— 我開始看到設計模式中的實際價值。作為一名應用程序開發人員,即使從不了解任何模式或者如何及何時使用這些模式,對您的職業生涯也沒有什么大的影響。但是,我發現了解這些模式以及 developerWorks 文章 “五種常見 PHP 設計模式” 中介紹的那些模式的優秀知識后(請參閱?參考資料),您可以完成兩件事情:
啟用高帶寬會話
如果了解設計模式,您將能夠更快地構建可靠的 OO 應用程序。但當整個開發團隊知道各種模式時,您可以突然擁有非常高的帶寬會話。您不再需要討論將到處使用的所有類。相反,您可以與其他人談論模式。“我要在這里引用一個單例(singleton),然后使用迭代器遍歷對象集合,然后……” 比遍歷構成這些模式的類、方法和接口快很多。單是通信效率一項就值得花時間以團隊的形式通過會話來研究模式。
減少痛苦的教訓
每個設計模式都描述了一種經過驗證的解決常見問題的方法。因此,您無需擔心設計是不是正確的,只要您已經選擇了提供所需優點的模式。
缺陷
有句諺語說得好:“當您手中拿著一把錘子時,所有事物看上去都像釘子”。當您認為自己找到一個優秀模式時,您可能會嘗試到處使用它,即使在不應當使用它的位置。記住您必須考慮正在學習的模式的使用目的,不要為了使用模式而把這些模式強行應用到應用程序的各個部分中。
本文將介紹可用于改進 PHP 代碼的五個模式。每個模式都將介紹一個特定場景??梢栽?下載?部分中獲得這些模式的 PHP 代碼。
要求
要發揮本文的最大功效并使用示例,需要在計算機中安裝以下軟件:
PHP V5 或更高版本(本文是使用 PHP V5.2.4 撰寫的)
壓縮程序,例如 WinZIP(用于壓縮可下載的代碼歸檔)
注:雖然您也可以使用純文本編輯器,但是我發現擁有語法高亮顯示和語法糾錯功能的編輯器真的很有幫助。本文中的示例是使用 Eclipse PHP Development Tools (PDT) 編寫的。
適配器模式
在需要將一類對象轉換成另一類對象時,請使用適配器模式。通常,開發人員通過一系列賦值代碼來處理此過程,如清單 1 所示。適配器模式是整理此類代碼并在其他位置重用所有賦值代碼的優秀方法。此外,它還將隱藏賦值代碼,如果同時還要設定格式,這樣可以極大地簡化工作。
清單 1. 使用代碼在對象之間賦值
class?AddressDisplay {private?$addressType;private?$addressText;public?function?setAddressType($addressType){$this->addressType?=?$addressType;}public?function?getAddressType(){return?$this->addressType;}public?function?setAddressText($addressText){$this->addressText?=?$addressText;}public?function?getAddressText(){return?$this->addressText;} }class?EmailAddress {private?$emailAddress;public?function?getEmailAddress(){return?$this->emailAddress;}public?function?setEmailAddress($address){$this->emailAddress?=?$address;} }$emailAddress?=?new?EmailAddress(); /*?Populate?the?EmailAddress?object?*/ $address?=?new?AddressDisplay();/*?Here's?the?assignment?code,?where?I'm?assigning?values?from?one?object?to?another...?*/$address->setAddressType("email"); $address->setAddressText($emailAddress->getEmailAddress());此示例將使用?AddressDisplay?對象把地址顯示給用戶。AddressDisplay?對象有兩部分:地址類型和一個格式化的地址字符串。
在實現模式(參見清單 2)后,PHP 腳本將不再需要擔心如何把?EmailAddress?對象轉換成?AddressDisplay?對象。那是件好事,尤其是在AddressDisplay?對象發生更改時或者控制如何把?EmailAddress?對象轉換成?AddressDisplay?對象的規則發生更改時。記住,以模塊化風格設計代碼的主要優點之一就是,在業務領域發生一些更改時或者需要向軟件中添加新功能時盡可能少的使用更改。即使在執行普通任務(例如把一個對象的屬性值賦給另一個對象)時,也請考慮使用此模式。
清單 2. 使用適配器模式
class?EmailAddressDisplayAdapter?extends?AddressDisplay {public?function?__construct($emailAddr){$this->setAddressType("email");$this->setAddressText($emailAddr->getEmailAddress());} } $email?=?new?EmailAddress(); $email->setEmailAddress("user@example.com");$address?=?new?EmailAddressDisplayAdapter($email);echo($address->getAddressType()?.?"\n")?; echo($address->getAddressText());圖 1 顯示了適配器模式的類圖。
圖 1. 適配器模式的類圖
替代方法
編寫適配器的替代方法 —— 并且是推薦方法 —— 是實現一個接口來修改行為,而不是擴展對象。這是一種非常干凈的、創建適配器的方法并且沒有擴展對象的缺點。使用接口的缺點之一是需要把實現添加到適配器類中,如圖 2 所示:
圖 2. 適配器模式(使用接口)
回頁首
迭代器模式
迭代器模式將提供一種通過對象集合或對象數組封裝迭代的方法。如果需要遍歷集合中不同類型的對象,則使用這種模式尤為便利。
查看上面清單 1 中的電子郵件和物理地址示例。在添加迭代器模式之前,如果要遍歷個人地址,則可能要遍歷物理地址并顯示這些地址,然后遍歷個人電子郵件地址并顯示這些地址,然后遍歷個人 IM 地址并顯示這些地址。非常復雜的遍歷!
相反,通過實現迭代器,您只需要調用?while($itr->hasNext())?并處理下一個條目?$itr->next()?返回。清單 3 中顯示了一個迭代器示例。迭代器功能強大,因為您可以添加要遍歷的新類型條目,并且無需更改遍歷條目的代碼。例如,在?Person?示例中,可以添加 IM 地址數組;只需更新迭代器,無需更改遍歷地址的任何代碼。
清單 3. 使用迭代器模式遍歷對象
class?PersonAddressIterator?implements?AddressIterator {private?$emailAddresses;private?$physicalAddresses;private?$position;public?function?__construct($emailAddresses){$this->emailAddresses?=?$emailAddresses;$this->position?=?0;}public?function?hasNext(){if?($this->position?>=?count($this->emailAddresses)?||?$this->emailAddresses[$this->position]?==?null)?{return?false;}?else?{return?true;}}public?function?next(){$item?=?$this->emailAddresses[$this->position];$this->position?=?$this->position?+?1;return?$item;}}如果把?Person?對象修改為返回?AddressIterator?接口的實現,則在將實現擴展為遍歷附加對象時無需修改使用迭代器的應用程序代碼。您可以使用一個混合迭代器,它封裝了遍歷清單 3 中列出的每種地址的迭代器。本文提供了此類應用示例(請參閱?下載)。
圖 3 顯示了迭代器模式的類圖。
圖 3. 迭代器模式的類圖
回頁首
裝飾器 (decorator) 模式
考慮清單 4 中的代碼樣例。這段代碼的目的是要把許多功能添加到 Build Your Own Car 站點的汽車中。每個汽車模型都有更多功能及相關價格。如果只針對兩個模型,使用?if then?語句添加這些功能十分平常。但是,如果出現了新模型,則必須返回查看代碼并確保語句對新模型工作正常。
清單 4. 使用裝飾器模式添加功能
require('classes.php');$auto?=?new?Automobile();$model?=?new?BaseAutomobileModel();$model?=?new?SportAutomobileModel($model);$model?=?new?TouringAutomobileModel($model);$auto->setModel($model);$auto->printDescription();進入裝飾器模式,該模式允許您通過一個優秀整潔的類將此功能添加到?AutomobileModel。每個類僅僅關注其價格、選項以及添加到基本模型的方式。
圖 4 顯示了裝飾器模式的類圖。
圖 4. 裝飾器模式的類圖
裝飾器模式的優點是可以輕松地同時跟蹤庫的多個裝飾器。
如果您擁有流對象的使用經驗,則一定使用過裝飾器。大多數流結構(例如輸出流)都是接受基本輸入流的裝飾器,然后通過添加附加功能來裝飾它 —— 例如從文件輸入流、從緩沖區輸入流,等等。
回頁首
委托模式
委托模式將提供一種基于各種條件委托行為的方法。考慮清單 5 中的代碼。這段代碼包含幾個條件。根據條件,代碼將選擇相應類型的對象來處理請求。
清單 5. 使用條件語句來發送送貨請求
pkg?=?new?Package("Heavy?Package"); $pkg->setWeight(100);if?($pkg->getWeight()?>?99) {echo(?"Shipping?"?.?$pkg->getDescription()?.?"?by?rail."); }?else?{echo("Shipping?"?.?$pkg->getDescription()?.?"?by?truck"); }使用委托模式,對象將內在化(internalize)此發送過程,方法為在調用如清單 6 中的?useRail()?之類的方法時設置對相應對象的內部引用。如果處理各個包的條件發生更改或者使用新的送貨類型時,則使用此模式尤為便利。
清單 6. 使用委托模式來發送送貨請求
require_once('classes.php');$pkg?=?new?Package("Heavy?Package"); $pkg->setWeight(100);$shipper?=?new?ShippingDelegate();if?($pkg->getWeight()?>?99) {$shipper->useRail(); }$shipper->deliver($pkg);委托將通過調用?useRail()?或?useTruck()?方法來切換處理工作的類,從而提供動態更改行為的優點。
圖 5 顯示了委托模式的類圖。
圖 5. 委托模式的類圖
回頁首
狀態模式
狀態模式類似于命令模式,但是意圖截然不同。考慮下面的代碼。
清單 7. 使用代碼來構建機器人
class?Robot? {private?$state;public?function?powerUp(){if?(strcmp($state,?"poweredUp")?==?0){echo("Already?powered?up...\n");/*?Implementation...?*/}?else?if?(?strcmp($state,?"powereddown")?==?0)?{echo("Powering?up?now...\n");/*?Implementation...?*/}}public?function?powerDown(){if?(strcmp($state,?"poweredUp")?==?0){echo("Powering?down?now...\n");/*?Implementation...?*/}?else?if?(?strcmp($state,?"powereddown")?==?0)?{echo("Already?powered?down...\n");/*?Implementation...?*/}}/*?etc...?*/}在此清單中,PHP 代碼表示變成一輛汽車的強大機器人的操作系統。機器人可以啟動、關閉、由汽車變成機器人以及由機器人變成汽車。代碼現已就緒,但是您會看到如果任何規則發生更改或者添加另一個狀態則會變得十分復雜。
現在查看清單 8,其中提供了相同的邏輯處理機器人的狀態,但是這一次把邏輯放入狀態模式。清單 8 中的代碼完成的工作與初始代碼相同,但是用于處理狀態的邏輯已經被放入每個狀態的一個對象中。為了演示使用設計模式的優點,假定不久以后,這些機器人發現它們不應在處于機器人模式時關閉。實際上,如果它們關閉,它們必須先切換到汽車模式。如果它們已經處于汽車模式下,則機器人將關閉。使用狀態模式,對代碼的更改十分微小。
清單 8. 使用狀態模式處理機器人的狀態
$robot?=?new?Robot(); echo("\n"); $robot->powerUp(); echo("\n"); $robot->turnIntoRobot(); echo("\n"); $robot->turnIntoRobot();?/*?This?one?will?just?give?me?a?message?*/ echo("\n"); $robot->turnIntoVehicle(); echo("\n");清單 9. 對一個狀態對象的微小更改
class?NormalRobotState?implements?RobotState {private?$robot;public?function?__construct($robot){$this->robot?=?$robot;}public?function?powerUp(){/*?implementation...?*/}public?function?powerDown()??{????????/*?First,?turn?into?a?vehicle?*/$this->robot->setState(new?VehicleRobotState($this->robot));$this->robot->powerDown();}public?function?turnIntoVehicle()??{/*?implementation...?*/}public?function?turnIntoRobot()?{/*?implementation...?*/} }圖 6 中一個不太明顯的地方就是狀態模式中的每個對象都有對上下文對象(機器人)的引用,因此每個對象都可以把狀態提升到相應的狀態。
圖 6. 狀態模式的類圖
回頁首
結束語
在 PHP 代碼中使用設計模式可以使代碼更容易閱讀、更易維護。通過使用已經建立的模式,您將從通用的設計結構中獲益,從而允許團隊的其他開發人員了解代碼的意圖。它還使您可以從其他設計者完成的工作中獲益,因此無需從失敗的設計理念中吸取教訓。
轉載于:https://my.oschina.net/u/1409620/blog/293575
總結
以上是生活随笔為你收集整理的另外五个 PHP 设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JQuery仿最新淘宝网首页带箭头幻灯片
- 下一篇: COM 学习小记录