鴨子
-
策略模式(
Strategy
)
前言
萬(wàn)事開(kāi)頭難,最近對(duì)這句話體會(huì)深刻!這篇文章是這個(gè)系列正式開(kāi)始介紹設(shè)計(jì)模式的第一篇,所以肩負(fù)著確定這個(gè)系列風(fēng)格的歷史重任,它在我腦袋里默默地醞釀了好多天,卻只搜刮出了一點(diǎn)兒不太清晰的輪廓,可是時(shí)間不等人,以后再多“迭代”幾次吧!在前面的隨筆里,我已經(jīng)提到了,這個(gè)系列準(zhǔn)備以《
Head First Design Patterns
》的結(jié)構(gòu)為主線,所以每個(gè)模式的核心故事都是取材于此書(shū),在此再次聲明一下。不管怎樣,宗旨是為了跟大家一起循序漸進(jìn)地去認(rèn)識(shí)設(shè)計(jì)模式。
上一篇:模式和原則,得到很多朋友的支持和鼓勵(lì),這里再次深表感謝。這里我還是想呼吁一下,希望大家看過(guò)后多提寶貴意見(jiàn),反對(duì)意見(jiàn)更好,關(guān)鍵是我們?cè)诨?dòng)中可以共同進(jìn)步,因?yàn)榻?jīng)驗(yàn)告訴我討論
(
爭(zhēng)論更甚
)
出來(lái)的火花,總是印象最深刻的。
其實(shí)策略模式是一個(gè)很簡(jiǎn)單的模式,也是一個(gè)很常用的模式,可謂短小精悍。我在介紹這個(gè)模式的同時(shí),為了加深大家對(duì)
OO
的理解,還會(huì)反復(fù)強(qiáng)調(diào)前面講過(guò)的設(shè)計(jì)原則和
GRASP
模式。這個(gè)系列的文章前后多少會(huì)有一些關(guān)聯(lián)的連續(xù)性,但是單獨(dú)一篇文章針對(duì)單一模式也一定是獨(dú)立的,所以不論大家想從前往后連續(xù)看也好,還是挑喜歡的跳著看,都沒(méi)有問(wèn)題。
“羅嗦了這么多,太唐僧了吧,快點(diǎn)開(kāi)始吧
…
”
(
爛西紅柿和臭雞蛋從四面八方飛來(lái)
)
模擬鴨子
Joe
是一名
OO
程序員,他為一家開(kāi)發(fā)模擬鴨子池塘游戲的公司工作,該公司的主要產(chǎn)品是一種可以模擬展示多種會(huì)游泳和呷呷叫的鴨子的游戲。這個(gè)游戲是使用標(biāo)準(zhǔn)的面向?qū)ο蠹夹g(shù)開(kāi)發(fā)的,系統(tǒng)里所有鴨子都繼承于
Duck
基類
,
系統(tǒng)的核心類圖如下:
如圖所示,在
Duck
基類里實(shí)現(xiàn)了公共的
quack()
和
swim()
方法,而
MallardDuck
和
RedheadDuck
可以分別覆蓋實(shí)現(xiàn)自己的
display()
方法,這樣即重用了公共的部分,又支持不同子類的個(gè)性化擴(kuò)展。從目前的情況看,這是一個(gè)很好的設(shè)計(jì),哈!
??
但是,商場(chǎng)如戰(zhàn)場(chǎng),不進(jìn)則退。
Joe
的公司最近的日子不好過(guò),盜版泛濫,再加上競(jìng)爭(zhēng)對(duì)手的圍追堵劫,已經(jīng)拖欠好幾個(gè)月工資了。因此,公司高層在一次集體“腐敗”后,決定一定要給系統(tǒng)增加一些超玄的功能,以徹底擊垮競(jìng)爭(zhēng)對(duì)手。經(jīng)過(guò)董事會(huì)討論,最終覺(jué)得如果能讓鴨子飛起來(lái),那么一定可以給對(duì)手致命一擊。于是
Joe
的上司對(duì)董事們拍著胸脯說(shuō):“這沒(méi)有問(wèn)題,
Joe
是一個(gè)
OO
程序員,這對(duì)他來(lái)說(shuō)太簡(jiǎn)單了!我們保證一周內(nèi)結(jié)束戰(zhàn)斗。”
接到任務(wù)的
Joe
絲毫不敢怠慢,研究了上級(jí)的指示以后,發(fā)現(xiàn)只要在
Duck
里增加一個(gè)
fly()
方法就可以搞定了,這樣所有繼承
Duck
的鴨子就都擁有了會(huì)飛的能力,哈!這回獎(jiǎng)金有盼頭啦!改進(jìn)后的系統(tǒng)類圖如下:
??? Joe
的上司很高興,帶著新產(chǎn)品給董事們演示去了……??
……
Joe
的上司:“我正在給董事們演示你會(huì)飛的鴨子,但是怎么有很多橡皮鴨子也在四處亂飛呢?你在耍我嗎?你還想不想混啦?!”
(
此處省略粗話
100
字
)
Joe
被嚇壞了,到手的獎(jiǎng)金泡湯了!冷靜下來(lái)的
Joe
發(fā)現(xiàn),原來(lái)在
Duck
類里增加的方法,也同樣被繼承于
Duck
的
RubberDuck
類繼承了,所以就有了會(huì)飛的橡皮鴨子,這是嚴(yán)重違反該系統(tǒng)“真實(shí)模擬各種鴨子”的原則的!那么該怎么辦呢?
Joe
很郁悶!他突然想到:如果在
RubberDuck
類里把
fly()
方法重寫(xiě)一下會(huì)如何?在
RubberDuck
類的
fly()
里讓橡皮鴨子什么都不做,不就一切
OK
了嗎!那以后再增加一個(gè)木頭鴨子呢?它不會(huì)飛也不會(huì)叫,那不是要再重寫(xiě)
quack()
和
fly()
方法,以后再增加其它特殊的鴨子都要這樣,這不是太麻煩了,而且也很混亂。
最終,
Joe
認(rèn)識(shí)到使用繼承不是辦法,因?yàn)樗纳纤就ㄖ聲?huì)決定以后每
6
個(gè)月就會(huì)升級(jí)一次系統(tǒng),以應(yīng)對(duì)市場(chǎng)競(jìng)爭(zhēng),所以未來(lái)的變化會(huì)很頻繁,而且還不可預(yù)知。如果以后靠逐個(gè)類去判斷是否重寫(xiě)了
quack()
或
fly()
方法來(lái)應(yīng)對(duì)變化,顯然混不下去!
(
Joe
這時(shí)很迷惑,為什么屢試不爽的繼承,在系統(tǒng)維護(hù)升級(jí)的時(shí)候,無(wú)法很好地支持重用呢?)
那么使用接口怎么樣?我可以把
fly()
方法放在接口里,只有那些會(huì)飛的鴨子才需要實(shí)現(xiàn)這個(gè)接口,最好把
quack()
方法也拿出來(lái)放到一個(gè)接口里,因?yàn)橛行喿邮遣粫?huì)叫的。就像下面這樣:
Joe
的上司知道后怒了:“你這樣做難道是希望所有需要
quack()
和
fly()
方法的鴨子都去重復(fù)實(shí)現(xiàn)這兩個(gè)方法的功能嗎?就這么幾個(gè)鴨子還好說(shuō),但是我們有幾十、上百個(gè)鴨子的時(shí)候你怎么辦?如果某個(gè)方法要做一點(diǎn)修改,難道你要重復(fù)修改上百遍嗎?你是不是瘋啦?”
呵呵!如果你是
Joe
,你該怎么辦?
我們知道,并不是所有的鴨子都會(huì)飛、會(huì)叫,所以繼承不是正確的方法。但是雖然上面的使用
Flyable
接口的方法,可以解決部分問(wèn)題
(
不再有會(huì)飛的橡皮鴨子
)
,但是這個(gè)解決方案卻徹底破壞了重用,它帶來(lái)了另一個(gè)維護(hù)的噩夢(mèng)!而且還有一個(gè)問(wèn)題我們前面沒(méi)有提到,難道所有的鴨子的飛行方式、叫聲等行為都是一模一樣的嗎?不可能吧!
說(shuō)到這里,為了能幫助
Joe
擺脫困境,我們有必要先停下來(lái),重新回顧一些面向?qū)ο笤O(shè)計(jì)原則。請(qǐng)您告訴我:“什么東西是在軟件開(kāi)發(fā)過(guò)程中是恒定不變的?”,您想到了嗎?對(duì),那就是變化本身,正所謂“計(jì)劃沒(méi)有變化快”,所以直面“變化這個(gè)事實(shí)”才是正道!
Joe
面對(duì)的問(wèn)題是,鴨子的行為在子類里持續(xù)不斷地改變,所以讓所有的子類都擁有基類的行為是不適當(dāng)?shù)模褂蒙厦娴慕涌诘姆绞剑制茐牧舜a重用。現(xiàn)在就需要用到我們的第一個(gè)設(shè)計(jì)原則:
Identify the aspects of your application that vary and separate them from what stays the same.
(
找到系統(tǒng)中變化的部分,將變化的部分同其它穩(wěn)定的部分隔開(kāi)。
)
換句話說(shuō)就是:“找到變化并且把它封裝起來(lái),稍后你就可以在不影響其它部分的情況下修改或擴(kuò)展被封裝的變化部分。”
盡管這個(gè)概念很簡(jiǎn)單,但是它幾乎是所有設(shè)計(jì)模式的基礎(chǔ),所有模式都提供了使系統(tǒng)里變化的部分獨(dú)立于其它部分的方法。
OK
!現(xiàn)在我們已經(jīng)有了一條設(shè)計(jì)原則,那么
Joe
的問(wèn)題怎么辦呢?就鴨子的問(wèn)題來(lái)說(shuō),變化的部分就是子類里的行為。所以我們要把這部分行為封裝起來(lái),省得它們老惹麻煩!從目前的情況看,就是
fly()
和
quack()
行為總是不老實(shí),而
swim()
行為是很穩(wěn)定的,這個(gè)行為是可以使用繼承來(lái)實(shí)現(xiàn)代碼重用的,所以,我們需要做的就是把
fly()
和
quack()
行為從
Duck
基類里隔離出來(lái)。我們需要?jiǎng)?chuàng)建兩組不同的行為,一組表示
fly()
行為,一組表示
quack()
行為。為什么是兩組而不是兩個(gè)呢?因?yàn)閷?duì)于不同的子類來(lái)說(shuō),
fly()
和
quack()
的表現(xiàn)形式都是不一樣的,有的鴨子嘎嘎叫,有的卻呷呷叫。有了這兩組行為,我們就可以組合出不同的鴨子,例如:我們可能想要實(shí)例化一個(gè)新的
MallardDuck(
野鴨
)
實(shí)例,并且給它初始化一個(gè)特殊類型的飛行行為
(
野鴨飛行能力比較強(qiáng)
)
。那么,如果我們可以這樣,更進(jìn)一步,為什么我們不可以動(dòng)態(tài)地改變一個(gè)鴨子的行為呢?換句話說(shuō),我們將在
Duck
類里包含行為設(shè)置方法,所以我們可以說(shuō)在運(yùn)行時(shí)改變
MallardDuck
的飛行行為,這聽(tīng)起來(lái)更酷更靈活了!那么我們到底要怎么做呢?回答這個(gè)問(wèn)題,先要看一下我們的第二個(gè)設(shè)計(jì)原則:
Program to an interface, not an implementation.
(面向接口編程,而不要面向?qū)崿F(xiàn)編程。)
嘿!對(duì)于這個(gè)原則,不論是耳朵還是眼睛,是不是都太熟悉了!“接口”這個(gè)詞已經(jīng)被賦予太多的含義,搞的大家一說(shuō)點(diǎn)兒屁事就滿嘴往外蹦“接口”。那么它到底是什么意思呢?我們這里說(shuō)的接口是一個(gè)抽象的概念,不局限于語(yǔ)言層面的接口
(
例如
C#
里的
interface)
。一個(gè)接口也可以是一個(gè)抽象類,或者一個(gè)基類也可以看作是一種接口的表現(xiàn)形式,因?yàn)榛愖兞靠梢杂脕?lái)引用其子類。要點(diǎn)在于,我們?cè)诿嫦蚪涌诰幊痰臅r(shí)候,可以使用多態(tài),那么實(shí)際運(yùn)行的代碼只依賴于具體的接口
(interface,
抽象類,基類
)
,而不管這些接口提供的功能是如何實(shí)現(xiàn)的,也就是說(shuō),接口將系統(tǒng)的不同部分隔離開(kāi)來(lái),同時(shí)又將它們連接在一起。我的神啊!接口真是太偉大了!
(
爛西紅柿和臭雞蛋從四面八方飛來(lái)
)
OK!
這回該徹底解決
Joe
的問(wèn)題了!
根據(jù)面向接口編程的設(shè)計(jì)原則,我們應(yīng)該用接口來(lái)隔離鴨子問(wèn)題中變化的部分,也就是鴨子的不穩(wěn)定的行為
(fly()
、
quack())
。我們要用一個(gè)
FlyBehavior
接口表示鴨子的飛行行為,這個(gè)接口可以有多種不同的實(shí)現(xiàn)方式,可以“橫”著分,也可以“豎”著分,管它呢!這樣做的好處就是我們將鴨子的行為實(shí)現(xiàn)在一組獨(dú)立的類里,具體的鴨子是通過(guò)
FlyBehavior
這個(gè)接口來(lái)調(diào)用這個(gè)行為的,因?yàn)?/span>
Duck
只依賴
FlyBehavior
接口,所以不需要管
FlyBehavior
是如何被實(shí)現(xiàn)的。如下面的類圖,
FlyBehavior
和
QuackBehavior
接口都有不同的實(shí)現(xiàn)方式!
Joe
已經(jīng)暈了,“你說(shuō)了這么多,全是大白話,來(lái)點(diǎn)代碼行不行,我要
C#
的!”。說(shuō)到這里,我們也該開(kāi)始徹底改造這個(gè)設(shè)計(jì)了,并會(huì)在最后附加部分代碼來(lái)幫助大家理解。??
第一步:我們要給
Duck
類增加兩個(gè)接口類型的實(shí)例變量,分別是
flyBehavior
和
quackBehavior
,它們其實(shí)就是新的設(shè)計(jì)里的“飛行”和“叫喚”行為。每個(gè)鴨子對(duì)象都將會(huì)使用各種方式來(lái)設(shè)置這些變量,以引用它們期望的運(yùn)行時(shí)的特殊行為類型
(
使用橫著飛,吱吱叫,等等
)
。
第二步:我們還要把
fly()
和
quack()
方法從
Duck
類里移除,因?yàn)槲覀円呀?jīng)把這些行為移到
FlyBehavior
和
QuackBehavior
接口里了。我們將使用兩個(gè)相似的
PerformFly()
和
PerformQuack()
方法來(lái)替換
fly()
和
qucak()
方法,后面你會(huì)看到這兩個(gè)新方法是如何起作用的。
第三步:我們要考慮什么時(shí)候初始化
flyBehavior
和
quackBehavior
變量。最簡(jiǎn)單的辦法就是在
Duck
類初始化的時(shí)候同時(shí)初始化他們。但是我們這里還有更好的辦法,就是提供兩個(gè)可以動(dòng)態(tài)設(shè)置變量值的方法
SetFlyBehavior()
和
SetQuackBehavior()
,那么就可以在運(yùn)行時(shí)動(dòng)態(tài)改變鴨子的行為了。
下面是修改后的
Duck
類圖:
我們?cè)倏纯凑麄€(gè)設(shè)計(jì)修改后的類圖:
最后大家再看看演示代碼,因?yàn)榇a比較多,就不貼出來(lái)了,大家可以下載后參考:
。下面是演示代碼的執(zhí)行結(jié)果:
這就是策略模式
前面說(shuō)了那么多,現(xiàn)在終于到了正式介紹我們今天的主角的時(shí)候啦!此刻心情真是好激動(dòng)啊!其實(shí)我們?cè)谇懊婢褪鞘褂?/span>
Strategy
模式幫
Joe
度過(guò)了難過(guò),真不知道他發(fā)了獎(jiǎng)金后要怎么感謝我們啊。
OK
!下面先看看官方的定義:
The Strategy Pattern defines
a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
(策略模式定義了一系列的算法,并將每一個(gè)算法封裝起來(lái),而且使它們還可以相互替換。策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化。)
怎么樣,有了前面
Joe
的經(jīng)歷,這個(gè)定義理解起來(lái)還不那么太費(fèi)勁吧?我想凡是認(rèn)真看到這里的人,應(yīng)該都能理解的。那么下面再畫(huà)蛇添足地羅嗦幾句,給那些還不太理解的朋友一個(gè)機(jī)會(huì)吧。
J
Context(
應(yīng)用場(chǎng)景
):
l????????
需要使用
ConcreteStrategy
提供的算法。
l????????
內(nèi)部維護(hù)一個(gè)
Strategy
的實(shí)例。
l????????
負(fù)責(zé)動(dòng)態(tài)設(shè)置運(yùn)行時(shí)
Strategy
具體的實(shí)現(xiàn)算法。
l????????
負(fù)責(zé)跟
Strategy
之間的交互和數(shù)據(jù)傳遞。
Strategy(
抽象策略類
)
:
l????????
定義了一個(gè)公共接口,各種不同的算法以不同的方式實(shí)現(xiàn)這個(gè)接口,
Context
使用這個(gè)接口調(diào)用不同的算法,一般使用接口或抽象類實(shí)現(xiàn)。
ConcreteStrategy(
具體策略類
)
:
l????????
實(shí)現(xiàn)了
Strategy
定義的接口,提供具體的算法實(shí)現(xiàn)。
?
還不理解?!我的神啊!那再看看下面的順序圖吧,這是最后的機(jī)會(huì)啦!
應(yīng)用場(chǎng)景和優(yōu)缺點(diǎn)
上面我們已經(jīng)看過(guò)了
Strategy
模式的詳細(xì)介紹,下面我們?cè)賮?lái)簡(jiǎn)單說(shuō)說(shuō)這個(gè)模式的優(yōu)缺點(diǎn)吧!怎么說(shuō)呢,人無(wú)完人,設(shè)計(jì)模式也不是萬(wàn)能的,每一個(gè)模式都有它的使命,也就是說(shuō)只有在特定的場(chǎng)景下才能發(fā)揮其功效。我們要使用好模式,就必須熟知各個(gè)模式的應(yīng)用場(chǎng)景。
對(duì)于
Strategy
模式來(lái)說(shuō),主要有這些應(yīng)用場(chǎng)景:
1、?
多個(gè)類只區(qū)別在表現(xiàn)行為不同,可以使用
Strategy
模式,在運(yùn)行時(shí)動(dòng)態(tài)選擇具體要執(zhí)行的行為。
(
例如
FlyBehavior
和
QuackBehavior)
2、?
需要在不同情況下使用不同的策略
(
算法
)
,或者策略還可能在未來(lái)用其它方式來(lái)實(shí)現(xiàn)。
(
例如
FlyBehavior
和
QuackBehavior
的具體實(shí)現(xiàn)可任意變化或擴(kuò)充
)
3、?
對(duì)客戶
(Duck)
隱藏具體策略
(
算法
)
的實(shí)現(xiàn)細(xì)節(jié),彼此完全獨(dú)立。
?
對(duì)于
Strategy
模式來(lái)說(shuō),主要有如下優(yōu)點(diǎn):
1、?
提供了一種替代繼承的方法,而且既保持了繼承的優(yōu)點(diǎn)
(
代碼重用
)
還比繼承更靈活
(
算法獨(dú)立,可以任意擴(kuò)展
)
。
2、?
避免程序中使用多重條件轉(zhuǎn)移語(yǔ)句,使系統(tǒng)更靈活,并易于擴(kuò)展。
3、?
遵守大部分
GRASP
原則和常用設(shè)計(jì)原則,高內(nèi)聚、低偶合。
對(duì)于
Strategy
模式來(lái)說(shuō),主要有如下缺點(diǎn):
1、?
因?yàn)槊總€(gè)具體策略類都會(huì)產(chǎn)生一個(gè)新類,所以會(huì)增加系統(tǒng)需要維護(hù)的類的數(shù)量。
?
???
備注:關(guān)于場(chǎng)景和優(yōu)缺點(diǎn),上面肯定說(shuō)得不夠全面,歡迎大家來(lái)補(bǔ)充。
.NET
框架里的應(yīng)用
Strategy
模式的應(yīng)用非常廣泛,也許大家有意無(wú)意之間一直都在使用。這里舉一個(gè)
.NET
框架里使用
Strategy
模式的例子,象這樣的例子其實(shí)還有很多,只要大家細(xì)心體會(huì)就一定會(huì)發(fā)現(xiàn)的。
如果寫(xiě)過(guò)程序,那么
ArrayList
類肯定都會(huì)用過(guò)吧,那么它的
Sort
方法想必大家也一定不陌生了。
Sort
方法的定義如下:
public
virtual
void
Sort (IComparercomparer)
可以看到
Sort
方法接收一個(gè)
IComparer
類型的參數(shù),那么這個(gè)
IComparer
接口是做什么用的呢?下面我們看一段程序,
下面的代碼示例演示如何使用默認(rèn)比較器和一個(gè)反轉(zhuǎn)排序順序的自定義比較器,對(duì) ArrayList中的值進(jìn)行排序。(完全引自MSDN:
ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref2/html/M_System_Collections_ArrayList_Sort_1_a2d90598.htm
)
?
?1
using
?System;
?2
using
?System.Collections;
?3
?4
public
?
class
?SamplesArrayList??
{
?5
?
?6
???
public
?
class
?myReverserClass?:?IComparer??
{
?7
?8
??????
//
?Calls?CaseInsensitiveComparer.Compare?with?the?parameters?reversed.
?9
??????
int
?IComparer.Compare(?Object?x,?Object?y?)??
{
10
??????????
return
(?(
new
?CaseInsensitiveComparer()).Compare(?y,?x?)?);
11
??????}
12
13
???}
14
15
???
public
?
static
?
void
?Main()??
{
16
?
17
??????
//
?Creates?and?initializes?a?new?ArrayList.
18
??????ArrayList?myAL?
=
?
new
?ArrayList();
19
??????myAL.Add(?
"
The
"
?);
20
??????myAL.Add(?
"
quick
"
?);
21
??????myAL.Add(?
"
brown
"
?);
22
??????myAL.Add(?
"
fox
"
?);
23
??????myAL.Add(?
"
jumps
"
?);
24
??????myAL.Add(?
"
over
"
?);
25
??????myAL.Add(?
"
the
"
?);
26
??????myAL.Add(?
"
lazy
"
?);
27
??????myAL.Add(?
"
dog
"
?);
28
?
29
??????
//
?Displays?the?values?of?the?ArrayList.
30
??????Console.WriteLine(?
"
The?ArrayList?initially?contains?the?following?values:
"
?);
31
??????PrintIndexAndValues(?myAL?);
32
?
33
??????
//
?Sorts?the?values?of?the?ArrayList?using?the?default?comparer.
34
??????myAL.Sort();
35
??????Console.WriteLine(?
"
After?sorting?with?the?default?comparer:
"
?);
36
??????PrintIndexAndValues(?myAL?);
37
38
??????
//
?Sorts?the?values?of?the?ArrayList?using?the?reverse?case-insensitive?comparer.
39
??????IComparer?myComparer?
=
?
new
?myReverserClass();
40
??????myAL.Sort(?myComparer?);
41
??????Console.WriteLine(?
"
After?sorting?with?the?reverse?case-insensitive?comparer:
"
?);
42
??????PrintIndexAndValues(?myAL?);
43
44
???}
45
?
46
???
public
?
static
?
void
?PrintIndexAndValues(?IEnumerable?myList?)??
{
47
??????
int
?i?
=
?
0
;
48
??????
foreach
?(?Object?obj?
in
?myList?)
49
?????????Console.WriteLine(?
"
\t[{0}]:\t{1}
"
,?i
++
,?obj?);
50
??????Console.WriteLine();
51
???}
52
53
}
54
55
56
/**/
/*
?
57
This?code?produces?the?following?output.
58
The?ArrayList?initially?contains?the?following?values:
59
????????[0]:????The
60
????????[1]:????quick
61
????????[2]:????brown
62
????????[3]:????fox
63
????????[4]:????jumps
64
????????[5]:????over
65
????????[6]:????the
66
????????[7]:????lazy
67
????????[8]:????dog
68
69
After?sorting?with?the?default?comparer:
70
????????[0]:????brown
71
????????[1]:????dog
72
????????[2]:????fox
73
????????[3]:????jumps
74
????????[4]:????lazy
75
????????[5]:????over
76
????????[6]:????quick
77
????????[7]:????the
78
????????[8]:????The
79
80
After?sorting?with?the?reverse?case-insensitive?comparer:
81
????????[0]:????the
82
????????[1]:????The
83
????????[2]:????quick
84
????????[3]:????over
85
????????[4]:????lazy
86
????????[5]:????jumps
87
????????[6]:????fox
88
????????[7]:????dog
89
????????[8]:????brown?
90
*/
?
怎么樣,大家看出來(lái)了吧,其實(shí)在這段代碼里,
ArrayList
相當(dāng)于
Strategy
模式中的
Context(
應(yīng)用場(chǎng)景
)
部分,而
IComparer
相當(dāng)于
Strategy(
抽象
策略類
)
部分,
myReverserClass
相當(dāng)于
ConcreteStrategy(
具體
策略類
)
部分。我們這里拋開(kāi)
myReverserClass
類的
Compare
方法
如何具體實(shí)現(xiàn)不談,我們只要知道這是一個(gè)具體
策略類,它提供了應(yīng)用場(chǎng)景需要的具體算法,它實(shí)現(xiàn)了抽象策略類接口,而應(yīng)用場(chǎng)景通過(guò)
抽象
策略類動(dòng)態(tài)調(diào)用到了
具體
策略類中的算法
。哈!所以這是一個(gè)十分典型的
Strategy
模式的應(yīng)用。
基于這個(gè)符合
Strategy
模式的結(jié)構(gòu),我們還可以提供很多種自定義的具體
策略類的實(shí)現(xiàn),只要這些類實(shí)現(xiàn)了
IComparer
接口,就可以在運(yùn)行時(shí)動(dòng)態(tài)設(shè)置給
ArrayList
類的
Sort
方法,在
Sort
方法中會(huì)根據(jù)具體
策略類實(shí)現(xiàn)的比較算法規(guī)則來(lái)對(duì)
ArrayList
中的數(shù)據(jù)進(jìn)行排序。
最后一個(gè)設(shè)計(jì)原則
關(guān)于
Strategy
模式的故事講到這里,應(yīng)該基本
OK
啦!下面我們?cè)倭男└邔哟蔚臇|西。什么是更高層次的東西?嘿!當(dāng)然是設(shè)計(jì)原則了!在前面總結(jié)
Strategy
模式的優(yōu)點(diǎn)的時(shí)候我們提到過(guò),
Strategy
模式不僅保留了繼承的優(yōu)點(diǎn),而且還提供了更靈活的擴(kuò)展能力。為什么會(huì)這樣呢?
Strategy
模式是怎么做到這一點(diǎn)的呢?哈!這是因?yàn)樗吧厦嬗腥恕卑。≌l(shuí)啊?它就是我們下面要介紹的重量級(jí)設(shè)計(jì)原則:
Favor composition over inheritance.
(優(yōu)先使用對(duì)象組合,而非類繼承)
關(guān)于組合和繼承,我們只要這樣來(lái)理解即可:組合是一種“
HAS-A
”關(guān)系,而繼承是一種“
IS-A
”關(guān)系。很明顯“
HAS-A
”要比“
IS-A
”更靈活一些。也就是說(shuō)在創(chuàng)建系統(tǒng)的時(shí)候,我們應(yīng)該優(yōu)先使用對(duì)象組合,因?yàn)樗粌H可以給你提供更多靈活性和擴(kuò)展性,而且還使你可以在運(yùn)行時(shí)改變行為
(
組合不同的對(duì)象
)
,這簡(jiǎn)直是酷斃了!但是也不是說(shuō)繼承就是不能用,只是說(shuō)應(yīng)該把繼承應(yīng)用在相對(duì)更穩(wěn)定,幾乎沒(méi)有變化的地方,例如前面的
Duck
類里的
Swim()
方法,因?yàn)榭梢钥隙ㄋ续喿右欢ǘ紩?huì)游泳,所以就沒(méi)有必要給這個(gè)行為提供基于
Strategy
模式的實(shí)現(xiàn)方式,因?yàn)槟菢幼龀耸浅绦蚋鼜?fù)雜以外,沒(méi)有什么意義。
BULLET POINTS
l???????
Knowing the OO basics does not make you a good OO designer.
l???????
Good OO designs are reusable,extensible and maintainable.
l???????
Patterns show you how to build systems with good OO design qualities.
l???????
Patterns are proven object oriented experience.
l???????
Patterns don’t give you code,they give you general solutions to design problems.You apply them to your specific application.
l???????
Patterns aren’t invented,they are discovered.
l???????
Most patterns and principles address issues of change in software.
l???????
Most patterns allow some part of a system to vary independently of all other parts.
l???????
We often try to take what varies in a system and encapsulate it.
l???????
Patterns provide a shared language that can maximize the value of your communication with other developers.
作者
王曉亮
/Justin
MSN:xiaoliang203@hotmail.com
Mail:xiaoliang.justin@gmail.com
參考資料
《
UML
和模式應(yīng)用》
《敏捷軟件開(kāi)發(fā)—原則、模式與實(shí)踐》
《
Head First Design Patterns
》
李建忠
老師的《
C#
面向?qū)ο笤O(shè)計(jì)模式縱橫談系列課程》