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