《PHP設計模式介紹》第十章 規(guī)范模式_PHP教程
推薦:《PHP設計模式介紹》第九章 觀測模式一些面向對象的編程方式,提供了一種構建對象間復雜網(wǎng)絡互連的能力。當對象們連接在一起時,它們就可以相互提供服務和信息。 通常來說,當某個對象的狀態(tài)發(fā)生改變時,你仍然需要對象之間能互
在一個應用軟件的成型過程中,一些意想不到的商業(yè)邏輯到處出現(xiàn)。比如,基于價格的考慮,這個任務必須減少項目;而那個任務也因為銷售稅而必須選擇合適的比率;而其它的任務也必須因為其他的特別條件而終止。一些商業(yè)規(guī)則是簡單的,只需要不到一兩個布爾比較關系就夠了,然而它的規(guī)則可能需要費時的估計,需要查詢數(shù)據(jù)庫或者用戶輸入數(shù)據(jù)來引導。
通過書寫代碼可以把抽象(比如一條商業(yè)規(guī)則)轉化為具體可見的東西。但是抽象物(比如購物方式,稅率,或者計算海運費等等)都有其進化的方式,而且這些改變很容易難倒一個不幸運的開發(fā)人員。為了保證安全可靠——到目前為止你在這本書所看到的——盡可能的分離封裝那些容易改變的模塊是個很完美的想法。而且,這的確也一個明智的應對商業(yè)規(guī)則的策略。
問題描述
有沒有明確的方式來封裝商業(yè)邏輯呢?有沒有一個容易改寫和重用的技術呢?
解決方案
規(guī)范模式是為驗證和選擇而開發(fā)的:
確認一個特殊的對象是否滿足一定的標準
從集合中選擇出滿足給定標準的元素。
規(guī)范模式能讓你有效的組織這些標準,并在你的應用程序中靈活的使用他們。
代碼重構技術已經(jīng)激發(fā)你的興趣,你決定使用它來提升代碼的清晰度和重用性。規(guī)范模式通過系統(tǒng)化進一步的深化了這一步,它系統(tǒng)把這個結構分解成一個個單獨的對象,這些對象能夠很方便的插入到你的應用程序的合適地方。很多情況下,在你的應用程序里,規(guī)范對象是參數(shù)化的,而且經(jīng)常被組合在一起來構建復雜的合乎邏輯的表達式。
相關知識
Eric Evans 和 Martin Fowler 發(fā)表過一篇關于規(guī)范模型的文章,地址是:
http://www.martinfowler.com/apsupp/spec.pdf
這個模式在Eric Evans的書本《動態(tài)驅動設計》(“Domain Driven Design”)的第224到273頁有詳細的介紹。
為了合理的全面覆蓋這個模式,這章被組織成合乎邏輯的三部分。第一部分通過一個純粹的實例來說明基本的模式概念。(Evans 和 Fowler 把這個稱為為“硬編碼規(guī)范Hard Coded Specification”)。接下來的部分演示了如何構建一個參數(shù)化規(guī)范模型,它提供了一個更加動態(tài)和靈活的框架來實現(xiàn)規(guī)范模式(或者因此而稱為“參數(shù)化規(guī)范”)的重用。最后一部分,我們開發(fā)了一個“方案工廠”(Policy Factory),它把許多規(guī)范對象集中成一個易于使用的包(package)。
Traveling to Warm Destinations(到溫暖的目的地去旅行)
最近,我和我的家人計劃去度一個假期,我的妻子想去一個“溫暖的地方”。雖然有無數(shù)旅行相關的站點,但是在我們訪問過的站點中沒有一個站點能夠為每一個目的地提供詳細的天氣信息。沒辦法,我們不得不轉到weather.com然后開始搜索,這是十分的不方便的。現(xiàn)在讓我們來改變這種情況,為一個假定的旅行站點增加一個天氣搜索功能。在這里我們是用規(guī)范模式這個指南來引導你編碼,從而比較旅行者期望的最低溫度和許多目的地的平均溫度
首先,我們創(chuàng)建一些非常簡單的對象。第一個是旅行者(a Traveler),它存儲了首選的最低溫度。
| // PHP5 class Traveler { public $min_temp; } |
接下來我們創(chuàng)建一個對象來表示目的地(Destination)。由于平均溫度是一個關鍵的標準,目的地的構建函數(shù)(__constructor)應該得到一個十二維的數(shù)組,該數(shù)組的每一個值對應一年里面每個月的平均溫度。
| class Destination { protected $avg_temps; public function __construct($avg_temps) { $this->avg_temps = $avg_temps; } } |
目的地(Destination)同樣也還要一個方法,通過調用這個方法能夠得到這個目的地在指定月份的平均溫度。
| class Destination { //... public function getAvgTempByMonth($month) { $key = (int)$month - 1; if (array_key_exists($key, $this->avg_temps)) { return $this->avg_temps[$key]; } } } |
最后,一次旅行(類Trip)就由一個旅行者(類Traveler),一個目的地(類Destination)和一個日期(a Date)聯(lián)合組成。
| class Trip { public $date; public $traveler; public $destination; } |
給出上面這些對象,你就可以通過Trip::date得到旅行的月份,并且你能夠比較目的地的月平均溫度和旅行者期望的最低溫度。(這個比較可能不是特別的復雜,但是你還是需要你自己親自去實現(xiàn))
讓我們看看如何用規(guī)范模式實現(xiàn)“溫暖目的地”的商業(yè)邏輯,并且看看如何應用這個模式來驗證每一個目的地并選擇出所有合適的目的地。
樣本代碼
規(guī)范模式的核心是一個帶有IsSatisfiedBy()方法的對象,IsSatisfiedBy()方法接收一個變量來評估并且返回一個基于規(guī)范標準的布爾值。
“目的地是足夠溫暖的”的標準可能就是:
| class TripRequiredTemperatureSpecification { public function isSatisfiedBy($trip) { $trip_temp = $trip->destination->getAvgTempByMonth( date(‘m’, $trip->date)); return ($trip_temp >= $trip->traveler->min_temp); } } |
下面是一些測試,用來檢驗這個規(guī)范是如何工作的。
一個最初的個體測試事例提供了一些目的地來一起工作:
| class TripSpecificationTestCase extends UnitTestCase { protected $destinations = array(); function setup() { $this->destinations = array( ‘Toronto’ => new Destination( array(24, 25, 33, 43, 54, 63, 69, 69, 61, 50, 41, 29)) ,’Cancun’ => new Destination( array(74, 75, 78, 80, 82, 84, 84, 84, 83, 81, 78, 76)) ); } } |
(構造這些目的地(Destination)需要在實例化的時候輸入一個包含每月平均溫度的數(shù)組。做為一個美國的作者,在這些例子中我選擇了華氏溫度。對應的,Vicki期望的華氏溫度70度等價于攝氏溫度21度)
下一個測試構建了一個旅行者(Traveler),并且設置了它的首選最低溫度和旅行日期同時也選擇了一個目的地。這最初的組合“最低溫度70度(華氏溫度),目的地多倫多(Toronto),日期二月中旬”會和期望的一樣,是不能通過的。
| class TripSpecificationTestCase extends UnitTestCase { // ... function TestTripTooCold() { $vicki = new Traveler; $vicki->min_temp = 70; $toronto = $this->destinations[‘Toronto’]; $trip = new Trip; $trip->traveler = $vicki; $trip->destination = $toronto; $trip->date = mktime(0,0,0,2,11,2005); $warm_enough_check = new TripRequiredTemperatureSpecification; $this->assertFalse($warm_enough_check->isSatisfiedBy($trip)); } } |
但是,接下來的這個組合“70度,二月中旬,Cancun ”就會通過,和我們期望的一樣。
| class TripSpecificationTestCase extends UnitTestCase { // ... function TestTripWarmEnough() { $vicki = new Traveler; $vicki->min_temp = 70; $cancun = $this->destinations[‘Cancun’]; $trip = new Trip; $trip->traveler = $vicki; $trip->destination = $cancun; $trip->date = mktime(0,0,0,2,11,2005); $warm_enough_check = new TripRequiredTemperatureSpecification; $this->assertTrue($warm_enough_check->isSatisfiedBy($trip)); } } |
分享:《PHP設計模式介紹》第八章 迭代器模式類中的面向對象編程封裝應用邏輯。類,就是實例化的對象,每個單獨的對象都有一個特定的身份和狀態(tài)。單獨的對象是一種組織代碼的有用方法,但通常你會處理一組對象或者集合。 屬性來自 SQL 查
- 相關鏈接:
- 教程說明:
PHP教程-《PHP設計模式介紹》第十章 規(guī)范模式
。