设计模式介绍
先看下面一個(gè)例子:
JOB開(kāi)發(fā)了一個(gè)模擬鴨子游戲,游戲中會(huì)出現(xiàn)各種各樣邊游戲邊呱呱叫的鴨子。該游戲系統(tǒng)采用了標(biāo)準(zhǔn)的OO(object Oriented)技術(shù)開(kāi)發(fā),系統(tǒng)中所有的鴨子都繼承與Duck類(lèi),核心類(lèi)圖如下:
?
隨著與其他公司的競(jìng)爭(zhēng)愈發(fā)激烈,公司高管認(rèn)為,游戲需要模擬會(huì)飛的鴨子,從而來(lái)甩開(kāi)競(jìng)爭(zhēng)對(duì)手。與是JOB理所當(dāng)然的在Duck類(lèi)中添加了個(gè)Fly()方法,然后讓所有的鴨子都繼承這個(gè)方法。修改后的核心類(lèi)圖如下:
但是,可怕的事情發(fā)生了,在演示中,游戲中新添加的鴨子角色RubberDuck【橡皮鴨子】,在天空飛行,因?yàn)樵贒uck類(lèi)添加了Fly()方法,所以所有繼承Duck類(lèi)的鴨子都具備了Fly()方法,也使得其他不會(huì)飛的鴨子也具備了飛行的能力。
于是,JOB想到了繼承,把RubberDuck的Fly()方法覆蓋,使它什么也不做。
這樣的確能解決眼前的問(wèn)題,但是如有還添加數(shù)十種行為不一樣的鴨子子類(lèi)的時(shí)候,我們又在每個(gè)鴨子子類(lèi)中去覆蓋Quack()和Fly()等方法?
通過(guò)這樣繼承來(lái)提供Durk的行為,做所帶來(lái)的缺點(diǎn)是:
1.代碼在多個(gè)子類(lèi)中復(fù)用;(不斷重寫(xiě)quack()或fly()等方法來(lái)應(yīng)對(duì)改變)
2.難以得知所有鴨子的全部行為,會(huì)帶來(lái)對(duì)Duck類(lèi)經(jīng)常的改動(dòng);
3.Duck類(lèi)的改變會(huì)牽一發(fā)而動(dòng)全身,造成其他鴨子不想要的改變。
JOB認(rèn)識(shí)到繼承并不是答案,于是他想到了用接口,把Fly()方法放到Flyable接口中,相應(yīng)的Quack()方法取出來(lái)放到Quackable接口,以后還可能添加其他的行為接口,當(dāng)有需要該行為的鴨子類(lèi),實(shí)現(xiàn)該行為接口即可,核心類(lèi)圖:
你覺(jué)得這種設(shè)計(jì)如何?
別忘了,JAVA接口不具有實(shí)現(xiàn)代碼,所以繼承接口無(wú)法達(dá)到代碼復(fù)用目的,就是說(shuō)如果你要修改某一個(gè)行為,你要往下追隨到該接口的 每一個(gè) 實(shí)現(xiàn)類(lèi)去一個(gè)一個(gè)修改,這顯然不是我們想要的結(jié)果。
這里我們引入第一條設(shè)計(jì)原則:
找出應(yīng)用可能需要改變之處,并把它獨(dú)立封裝起來(lái),不要和不需要變化的代碼混合在一起。
在上述的問(wèn)題,我們可知,鴨子的行為(飛行、叫聲)會(huì)隨著鴨子的不同而改變,所以我們需要把它獨(dú)立出來(lái),建立一組新類(lèi)代表每一個(gè)行為。比方說(shuō),我們需要一個(gè)類(lèi)會(huì)呱呱叫,一個(gè)類(lèi)實(shí)現(xiàn)吱吱叫,一個(gè)類(lèi)實(shí)現(xiàn)安靜。
從現(xiàn)在開(kāi)始,鴨子的行為被分離到了獨(dú)立的一組類(lèi)中,這組類(lèi)用來(lái)實(shí)現(xiàn)鴨子的行為,也就是說(shuō),在鴨子類(lèi)中我們需要一個(gè)設(shè)定行為的方法(因?yàn)樾袨楹网喿宇?lèi)本身已經(jīng)獨(dú)立分開(kāi)了)。這樣也使得我們可以動(dòng)態(tài)的改變鴨子行為。
為了達(dá)到以上目標(biāo),我們映入第二條設(shè)計(jì)原則:
針對(duì)接口編程,不要針對(duì)實(shí)現(xiàn)編程。(這里的接口可以是抽象類(lèi)和java 接口)
根據(jù)這條原則,我們?yōu)槊恳粋€(gè)行為定義一個(gè)接口,如為fly行為定義接口FlyBehavior,為Quack行為定義接口QuackBehavior。而這些行為都必須實(shí)現(xiàn)其中一個(gè)接口。例如如下的fly行為和Quack行為:
在這種設(shè)計(jì)之下,鴨子的行為由實(shí)現(xiàn)FlyBehavior和QuackBehavior接口的實(shí)現(xiàn)類(lèi)來(lái)完成,而不會(huì)綁死在Duck的子類(lèi)中。這樣的設(shè)計(jì)可以使得這些行為可以復(fù)用,而且增加一個(gè)行為,也不會(huì)造成其他鴨子子類(lèi)不必要的改變(當(dāng)該行為不適用該鴨子,不必要通過(guò)重寫(xiě)來(lái)配合改變)。
鴨子的行為我們已經(jīng)解決的,最后我們需要的是整合鴨子的行為。
第一步:我們要給Duck類(lèi)增加兩個(gè)接口類(lèi)型的實(shí)例變量,分別是flyBehavior和quackBehavior,它們是新的設(shè)計(jì)里的“飛行”和“叫喚”行為。每個(gè)鴨子對(duì)象都將會(huì)使用多態(tài)的方式在運(yùn)行時(shí)獲得所需要的行為類(lèi)型的引用。
第二步:我們還要把fly()和quack()方法從Duck類(lèi)里移除,因?yàn)槲覀円呀?jīng)把這些行為移到FlyBehavior和QuackBehavior接口里了。我們將使用兩個(gè)相似的performFly()和performQuack()方法來(lái)替換fly()和qucak()方法。
第三步:我們要考慮什么時(shí)候初始化flyBehavior和quackBehavior變量。最簡(jiǎn)單的辦法就是在Duck類(lèi)初始化的時(shí)候同時(shí)初始化他們。但是我們這里還有更好的辦法,就是提供兩個(gè)可以動(dòng)態(tài)設(shè)置變量值的方法SetFlyBehavior()和SetQuackBehavior(),那么就可以在運(yùn)行時(shí)動(dòng)態(tài)改變鴨子的行為了。
整合后的核心類(lèi)圖:
?
其中PerformFly()方法只需要這樣設(shè)置:
preformFly(){FB.fly(); }?
setFlyBehavior(FlyBehavior flybehavior)方法只需要這樣設(shè)置:
setFlyBehavior(FlyBehavior flybehavior){FB=flybehavior; }?PerformQuack()方法和SetQuackBehavior(QuackBehavior quackbehavior)與之類(lèi)似。
當(dāng)我們需要建立一個(gè)不會(huì)飛和吱吱叫的橡皮鴨子時(shí),我們只需要:
Duck rubberDuck=new RubberDuck(); rubberDuck.setFlyBehavior(new FlyNoWay()); rubberDuck.setQuackBehavior(new Squeak());rubberDuck.display(); rubberDuck.preformFly(); rubberDuck.preformQuack();
? 如果我們需要增加一個(gè)新的鴨子子類(lèi),和增加一種該鴨子子類(lèi)的行為,按照以上方法是很容易而且簡(jiǎn)單的辦到。而且不會(huì)應(yīng)該到其他鴨子子類(lèi)。
到了這里,你已經(jīng)學(xué)會(huì)了第一種設(shè)計(jì)模式:Strategy Pattern(策略模式)
Strategy Pattern定義如下:定義算法族,分別封裝起來(lái),讓它們之間可以相互替換,此模式讓算法獨(dú)立于使用它的客戶(hù)而獨(dú)立變化
(其中算法族,對(duì)于上例來(lái)說(shuō),就是鴨子的一組行為。)
我們還可以從上例學(xué)到一個(gè)很重要的技巧。上例中,每一個(gè)鴨子都有一個(gè)FlyBehavior而且有一個(gè)QuackBehavior,讓鴨子將飛行和叫委托給它們代為處理。當(dāng)你將兩個(gè)類(lèi)結(jié)合起來(lái)使用,這就是組合(composition)。這種做法和“繼承”不同的地方在于,鴨子的行為不是繼承而來(lái),而是和適當(dāng)?shù)男袨閷?duì)象“組合”而來(lái)。
這就是我們的第三個(gè)設(shè)計(jì)原則:
多用組合,少用繼承。
通過(guò)上例的學(xué)習(xí),你或者已經(jīng)對(duì)設(shè)計(jì)模式有一定的了解。
設(shè)計(jì)模式并不是代碼,而是針對(duì)設(shè)計(jì)問(wèn)題的通用的解決方案。可以使開(kāi)發(fā)人員開(kāi)發(fā)出具有可復(fù)用性,可擴(kuò)充性,可維護(hù)性的良好的OO系統(tǒng),同時(shí)模式可以使開(kāi)發(fā)人員之間有共享的語(yǔ)言,最大化溝通的價(jià)值。這也是我們學(xué)習(xí)設(shè)計(jì)模式的目的。
?
轉(zhuǎn)載于:https://www.cnblogs.com/hellocsl/p/3598378.html
總結(jié)
- 上一篇: 算法与数据结构--数组和链表的区别
- 下一篇: iOS 7 新版微信 URL 不支持跳转