設計模式學習(一)——策略模式


  前段時間買了一本書《Head First設計模式》,看了第一章后才對設計模式的概念有少許了解:它其實是開發過程中很多前人的經驗與智慧的總結,幫助你在開發時采取更好的方式去設計各個類、方法、以及它們之間的調用、實現方式,讓代碼保持靈活性的同時又能更好地復用。基於學過一塊知識一定要用文字記錄、總結、鞏固,而不是走馬觀花的原則,趁最近終於有空,特將前一段時間看的關於“策略模式”的內容總結於此。

場景描述

A公司要做一套模擬鴨子的游戲,游戲中會出現各種鴨子,一邊游泳戲水,一邊呱呱叫,還有一些會飛。

方案1 繼承

設計一個超類Duck,包含方法quack()、swim()、fly()分別模擬鴨子的叫、游泳、飛行等行為,再包含一個抽象類display(),用於展示各個鴨子不同的外觀,讓每個鴨子子類繼承父類時實現display()。

弊端:

這樣做的好處是,每個鴨子子類繼承父類時就同時擁有了父類的方法,可以達到代碼復用的目的,但是這樣會使某些並不適合該行為的子類也具有該行為。如果某些子類鴨子,如“橡皮鴨”,它不具備某些功能(如飛行),它就不應該擁有這個飛行的功能。當然,你可以在子類中通過@Override覆蓋這個方法。但是當各個不同的子類都需要通過覆蓋修改不同的方法時,就會非常繁瑣,而且容易出現紕漏,且這些新覆蓋的方法不能被復用。如“橡皮鴨”只會叫不會飛,“木頭鴨”不會叫也不會飛。以后每當有新的鴨子子類出現,你都要去檢查並可能需要覆蓋這些方法,想想都讓人抓狂。

方案2 接口

在超類Duck中將quack()、fly()等可變的方法用接口Quackable(),Flyable()來代替,然后在每個鴨子子類中,如果具有“飛行”或“叫”這個功能就實現“飛行“或”叫“這個接口。

弊端:

代碼無法復用,如果有100個子類,都具有飛行的行為,你就需要重復100次代碼。

設計模式來幫忙

設計原則一:找出程序中可能需要變化的地方和不需要變化的地方,將它們獨立開來。讓系統中的某部分改變不會影響其他部分。

由於fly()和quack()會隨着鴨子的不同而改變,所以把這兩個行為從Duck類中分開,建一組新類來代表各個行為。

設計原則二:針對接口編程,而不是針對實現。

利用多態,針對超類型編程,執行時根據實際狀況執行到真正的行為,不會被綁死在超類型的行為上。以前的做法是:行為來自超類的具體實現或是繼承某個接口並由子類自行實現。這兩種方法都捆綁於”實現“,無法方便地更改行為。現在我們利用接口代表每個行為,比如FlyBehavior,QuackBehavior,然后讓各個行為類實現這些接口,然后在Duck類中只要定義這個接口的實例變量即可,這樣在各個鴨子子類中如果想擁有某種特定的行為,只要用這個接口實例變量去引用具體的行為類即可。

設計原則三:多用組合,少用繼承。

飛行和叫這兩種不同的行為,我們分別為其建立兩組不同的行為類,然后在Duck類中通過接口實例變量結合起來,這就是”組合“。使得系統具有很大的彈性,還可以”在運行時動態地改變行為“。

下面就是經過重新設計后的應用代碼:

1.應用的目錄結構圖

2.接口FlyBehavior

1 public interface FlyBehavior {
2     public void fly();
3 }

 3.接口QuackBehavior

1 public interface QuackBehavior {
2     public void quack();
3 }

4.Fly行為的一個實現——FlyNoWay

1 public class FlyNoWay implements FlyBehavior{
2     @Override
3     public void fly() {
4         System.out.println("I can not fly.");
5     }
6 }

5.Fly行為的另一個實現——FlyWithWings

1 public class FlyWithWings implements FlyBehavior {
2     @Override
3     public void fly() {
4         System.out.println("I can fly!");
5     }
6 }

6.Fly行為的又另一個實現——FlyRocketPowered 

1 public class FlyRocketPowered implements FlyBehavior{
2     @Override
3     public void fly() {
4         System.out.println("I am flying with a rocket.");
5     }
6 }

7.父類Duck

 1 public abstract class Duck {
 2     FlyBehavior flyBehavior;
 3     QuackBehavior quackBehavior;
 4     public abstract void display();
 5     public void performFly(){
 6         flyBehavior.fly();
 7     }
 8     public void performQuack(){
 9         quackBehavior.quack();
10     }
11     public void setFlyBehavior(FlyBehavior fb){
12         this.flyBehavior = fb;
13     }
14     public void setQuackBehavior(QuackBehavior qb){
15         this.quackBehavior=qb;
16     }
17 }

8.Duck的一個子類——綠頭鴨MallardDuck

 1 public class MallardDuck extends Duck{
 2     public MallardDuck() {
 3         flyBehavior = new FlyWithWings();
 4         quackBehavior = new QuackWithGuaGua();
 5     }
 6 
 7     @Override
 8     public void display() {
 9         System.out.println("I am a MallardDuck.");
10         
11     }
12 }

9.Duck的另一個子類——模型鴨ModelDuck

 1 public class ModelDuck extends Duck {
 2     public ModelDuck() {
 3         flyBehavior = new FlyNoWay();
 4         quackBehavior = new QuackNoWay();
 5     }
 6     
 7     @Override
 8     public void display() {
 9         System.out.println("I am a ModelDuck.");        
10     }
11 }

10.應用模擬器(執行主類):MiniDuckSimulator

 1 public class MiniDuckSimulator {
 2 
 3     public static void main(String[] args) {
 4         Duck mallardDuck = new MallardDuck();
 5         mallardDuck.display();
 6         mallardDuck.performFly();
 7         mallardDuck.performQuack();
 8         Duck modelDuck = new ModelDuck();
 9         modelDuck.display();
10         modelDuck.performFly();
11         modelDuck.performQuack();
12         modelDuck.setFlyBehavior(new FlyRocketPowered());
13         modelDuck.performFly();
14     }
15 
16 }

執行代碼,得到的結果如下:

總結

策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。這是書里給出的策略模式的定義,依我個人的理解,這種模式的關鍵就是要將系統中的可變行為抽象出來,單獨進行封裝,用一組行為類(算法族)來實現該特定的接口,這樣任何類(如Duck)如果想擁有這些算法族中的某個算法,都可以通過定義接口實例變量而擁有該整個算法族,在子類中再對該變量進行賦值。像這個例子里,通過定義了FlyBehavior,這樣以后任何想有飛行行為的類如飛機,都可以調用這個接口及實現它的各個飛行類。且飛行行為的變化可以通過增加新的飛行類來實現,不會對其他部分(如quack()、display()等行為,亦或是已擁有某些特定飛行行為的對象)造成任何影響,即”算法的變化獨立於使用算法的客戶“。而且各個飛行行為之間也可以互相替換。即 setFlyBehavior(Flybehavior fb)

可見,設計模式的應用讓整個項目的代碼擁有了極大的靈活性,且達到了代碼復用的效果。設計模式其實是一種設計上的思維方式,是前人的智慧和經驗的總結,其真正的精髓不是看過就能學會的,還是需要在實際應用中不斷地實踐摸索,慢慢體會。

文章中如果有任何問題,歡迎大家指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM