《PHP設(shè)計(jì)模式介紹》第七章 策略模式_PHP教程
推薦:《PHP設(shè)計(jì)模式介紹》第六章 偽對象模式面向?qū)ο蟮木幊讨载S富多彩,部分是由于對象間的相互聯(lián)系與作用。一個(gè)單一的對象就能封裝一個(gè)復(fù)雜的子系統(tǒng),使那些很復(fù)雜的操作能夠通過一些方法的調(diào)用而簡化。(無所不在的數(shù)據(jù)庫連接就是這
在編寫面向?qū)ο蟮拇a的時(shí),有些時(shí)候你需要一個(gè)能夠自己根據(jù)不同的條件來引入不同的操作對象實(shí)例。例如,一個(gè)菜單功能能夠根據(jù)用戶的“皮膚”首選項(xiàng)來決定是否采用水平的還是垂直的排列形式,或者一個(gè)計(jì)費(fèi)系統(tǒng)可以自行根據(jù)用戶的收貨地址來決定稅率。
一般來講,一個(gè)控制菜單的對象實(shí)例包括了add(), delete(), 和 replace()等菜單元素;并通過set()進(jìn)行配置,用render()來管理顯示模式。無論你想生成什么樣子的菜單,你都可以用同一個(gè)對象類來處理。不同菜單的對象實(shí)例只是一些方式函數(shù)的運(yùn)算規(guī)則不同罷了,至少在剛才的例子里面render()函數(shù)是不同的。
但是如果你需要增加菜單的顯示模式種類,或者你需要根據(jù)用戶的國家、省份等信息來判斷菜單排列的順序的時(shí)候,該怎么做呢?而且如果有許多的方式函數(shù)都是經(jīng)常變化的,那么簡單的類封裝將變得復(fù)雜、難易理解和升級的。
問題
怎么輕松地改變對象實(shí)例的執(zhí)行過程,因而在代碼執(zhí)行的時(shí)候動態(tài)地改變執(zhí)行過程?一旦實(shí)現(xiàn)了這個(gè)功能,如果去編寫這樣的類定義從而讓維護(hù)和升級變得非常簡單呢?
解決辦法
當(dāng)一個(gè)類封裝了多個(gè)操作的時(shí)候,對象實(shí)例可以動態(tài)地選擇這些操作來進(jìn)行,可以用策略模式來把對象本身和運(yùn)算規(guī)則區(qū)分開來。或者,更簡單的處理是類里面定義的方式函數(shù)用case語句來進(jìn)行控制。當(dāng)然更簡單的方法是使用策略模式。
策略模式功能非常強(qiáng)大,因?yàn)檫@個(gè)設(shè)計(jì)模式本身的核心思想就是面向?qū)ο缶幊痰亩嘈涡缘乃枷搿?/p>
就在編程領(lǐng)域之外,有許多例子是關(guān)于策略模式的。如果我需要在清晨從家里去上班,我可以有幾個(gè)策略可以考慮:我可以開車,乘坐公交車,走路,汽車或者甚至是搭乘直升飛機(jī)。每個(gè)策略都可以得到相同的結(jié)果,但是它們使用了不同的資源。選擇策略的依據(jù)是費(fèi)用,時(shí)間,使用工具還有每種方式的方便程度 。一個(gè)很好的策略也許在第二天就不能再被使用的,所以策略的選擇是相對的。
你已經(jīng)在前面的工廠模式章節(jié)看到了和策略模式相似的例子:因?yàn)椴煌匦缘馁M(fèi)用計(jì)算方式不同,所以Monopoly游戲的框架使用了許多相似的特性類,但是因?yàn)橘M(fèi)用的計(jì)算不是從類本身獲得,所以這個(gè)費(fèi)用計(jì)算相對來說是一個(gè)TemplateMethod 設(shè)計(jì)模式。
例子
舉例子說明,讓我們做一個(gè)存儲PHP參數(shù)的cache。這個(gè)cahce類需要把變量以PHP識別的方式寫入到一個(gè)文件當(dāng)中,所以你可以在以后加載該文件并使用它。這個(gè)類還應(yīng)該可以讓你為每個(gè)數(shù)據(jù)加個(gè)標(biāo)識符和存儲的方式。
數(shù)據(jù)緩存
注:緩存是為了在接下來的操作中繼續(xù)使用而對資源進(jìn)行緩存。你可以通過建立和使用緩存來節(jié)省直接從原數(shù)據(jù)庫獲取數(shù)據(jù)的時(shí)間。這方面的例子最常見的就是訪問數(shù)據(jù)庫或者解析大的XML文檔,或者大的配置文件。
緩存也會出現(xiàn)一個(gè)問題:你的緩存可能會失去與原數(shù)據(jù)的同步。或者緩存需要使用太多內(nèi)存。
最開始,我們開發(fā)一個(gè)緩存操作,并不使用策略模式。
因?yàn)槟憧赡苄枰彺娴牟恢挂粋(gè)值,所以你需要使用標(biāo)識符來標(biāo)識出你需要指定的元素。在這個(gè)例子中,標(biāo)識符就是’application_config’。下面試一個(gè)如果使用cache的例子。
// PHP4 $config_cache =& new VarCache(‘application_config’); if ($config_cache->isValid()) { $config = $config_cache->get(); } else { $config = slow_expensive_function_to_get_config(); $config_cache->set($config); } |
這個(gè)代碼生成了一個(gè)新的VarCache對象存放在$config_cache變量里面。這個(gè)數(shù)據(jù)在緩存中的標(biāo)識符是 ‘application_config’。如果在緩存里面有這個(gè)數(shù)據(jù), isValid() 將返回真( true )并且獲取緩存中的數(shù)據(jù)。反之,值被重新獲取并寫入緩存當(dāng)中,以便下次使用。
按照一般的需求,讓我們開始編寫這段代碼來進(jìn)行測試。首先,如果緩存中沒有該數(shù)據(jù), isValid() 方式函數(shù)應(yīng)該返回非值(false)。
class VarCacheTestCase extends UnitTestCase { function TestUnsetValueIsInvalid() { $cache =& new VarCache(‘foo’); $this->assertFalse($cache->isValid()); } |
因?yàn)閂arCache現(xiàn)在沒有代碼,所以最簡單的方式就是先構(gòu)造一個(gè)方式函數(shù)。
class VarCache { function isValid() {} } |
這樣,我們就可以繼續(xù)了。
class VarCacheTestCase extends UnitTestCase { function TestUnsetValueIsInvalid() { /* ... */ } function TestIsValidTrueAfterSet() { $cache =& new VarCache(‘foo’); $cache->set(‘bar’); $this->assertTrue($cache->isValid()); } |
上面的測試校驗(yàn)了緩存的數(shù)據(jù)是否是可用的。
開始編寫cache類的主要部分。VarCache 引入一個(gè)標(biāo)識符, 所以constructor了一個(gè)應(yīng)該記錄它的對象實(shí)例。這里面還有一個(gè)set的方式函數(shù),用來把數(shù)據(jù)存入緩存,或者當(dāng)數(shù)據(jù)存在時(shí),修改緩存當(dāng)中的數(shù)據(jù)。
class VarCache { |
對象實(shí)例的參數(shù)$_name 存放了緩存的標(biāo)識符。在這個(gè)簡單的操作中, $_name 被用來生成文件名(在實(shí)際的使用可能會數(shù)據(jù)庫或者其它的數(shù)據(jù)源代替) set() 使用 fopen() 和 fclose() 來 “訪問” 基于$_name的文件。當(dāng)調(diào)用set()后, file_exists()在VarCache::isValid()里面調(diào)用返回真(true)。
運(yùn)行這個(gè)測試來產(chǎn)生一個(gè)我們預(yù)期的結(jié)果;但是實(shí)際情況是報(bào)錯(cuò)!為什么呢?第一次運(yùn)新的時(shí)候沒有生成文件,所以第二次運(yùn)行的時(shí)候找不到文件,顯然我們不希望這種情況出現(xiàn)。我們期望的是每一次運(yùn)行代碼都是互不影響的。
幸運(yùn)的是,把總體測試框架和特定功能的簡單測試結(jié)合起來,我們就可以得到靈活的測試環(huán)境,并且在以后的測試中方便地使用。UnitTestCase::setUp()實(shí)現(xiàn)框架的初始化,而UnitTestCase::tearDown()實(shí)現(xiàn)具體的測試過程。
分享:《PHP設(shè)計(jì)模式介紹》第五章 注冊模式我們通常認(rèn)為避免使用全局變量是一種好的選擇,因此,對象經(jīng)常被作為參數(shù)從一段代碼傳遞到另一段。但是傳遞實(shí)例的一個(gè)問題就是對象有時(shí)候不知道將要傳遞給誰——?經(jīng)過一個(gè)函數(shù)后才被傳
- PHPNOW安裝Memcached擴(kuò)展方法詳解
- php記錄頁面代碼執(zhí)行時(shí)間
- PHP中獎(jiǎng)概率的抽獎(jiǎng)算法程序代碼
- apache設(shè)置靜態(tài)文件緩存方法介紹
- php對圖像的各種處理函數(shù)代碼小結(jié)
- PHP 關(guān)于訪問控制的和運(yùn)算符優(yōu)先級介紹
- 關(guān)于PHP語言構(gòu)造器介紹
- php/js獲取客戶端mac地址的實(shí)現(xiàn)代碼
- php5.5新數(shù)組函數(shù)array_column使用
- PHP preg_match的匹配多國語言的技巧
- php 中序列化和json使用介紹
- php采集文章中的圖片獲取替換到本地
- 相關(guān)鏈接:
- 教程說明:
PHP教程-《PHP設(shè)計(jì)模式介紹》第七章 策略模式。