[Design Pattern] 策略模式(Strategy Pattern) 把跑車行為裝 ...
文章推薦指數: 80 %
範例. 舉例說明,假如公司開發了一款賽車遊戲,遊戲內一定包含各種不同款跑車而每款車都有加速 ...
摘要:[DesignPattern]策略模式(StrategyPattern)把跑車裝箱吧
前言
最近在閱讀對於設計模式與程式碼重構的相關書籍,為了方便複習順便在此寫下對於設計模式的實作方法說明。
首先為何要套用設計模式呢?在OO的設計模式下,撰寫的程式碼是以"降低耦合提高聚合"為最大目標,我們可以透過套用設計模式的方法來趨近這個目標,第一個就先來介紹策略模式吧,使用策略模式主要的目標就是將有相同行為的動作封裝降低類別間的耦合,並且可以因應執行時期的需求去動態切換該使用哪種行為,聽了一定還是很模糊吧?讓我舉以下範例來說明。
範例
舉例說明,假如公司開發了一款賽車遊戲,遊戲內一定包含各種不同款跑車而每款車都有加速、剎車、噴射的動作,那以類別來劃分時候應該怎麼做呢?我們可以透過定義一個包含以上行為的父類別Car去實作這些行為,再透過子類別的繼承讓所有子類別都可以使用到這些方法。
先來看一下類別圖的樣子,如下圖:
程式碼:
publicabstractclassCar
{
publicvoidSpeedUp()
{
Console.WriteLine("加速中");
}
publicvoidSpeedDown()
{
Console.WriteLine("減速中");
}
publicvoidUseNOS()
{
Console.WriteLine("開啟氮氣加速");
}
}
publicclassPorscheCar:Car
{
}
publicclassAmbulanceCar:Car
{
}
如此就能夠經由繼承讓子類別可以去呼叫父類擁有的方法了,乍看之下這樣的類別應該可以因應目前遊戲的需求,但是日子久了...遊戲就需要增加更多種不同的玩法以保持玩家的新鮮感,所以這時候老闆就要你修改功能!最後在團隊討論完後決定要增加攻擊的行為,而我們就自然地在Car類別加入了Atk()方法,修改類別圖如下:
但這時候問題來了,當我們增加攻擊方法讓跑車能攻擊後卻發現有些不該可攻擊的車種確能攻擊,例如救護車能攻擊好像不太合邏輯?
會發生此種情況是因為救護車也繼承Car類別的攻擊方法,這時候如要處理這種情況一般我們可以Override父類別的Atk()方法即可,如下:
publicclassAmbulanceCar:Car
{
publicoverridevoidAtk()
{
//什麼事都不做...
}
}
但是這並不是一種最好的解決方法,可以想想如果當不需要攻擊方法的車種有很多款甚至以後也會陸續加入新車種時,是不是每次都要override這個攻擊方法呢?繼續換個想法,那如果使用介面呢?使用介面定義Car的行為再由有實作該介面的類別去實作這些動作,如下:
這樣的做法其實也不恰當,因為透過介面每個類別也必須要去實作方法,一樣當加入新的行為時還是要去一一增加實作此介面的類別,當車款類別一多之後將會變得非常麻煩。
那到底應該用什麼做法才比較好呢?這時候就是策略模式出場的時機了!
於 HeadFirstDesignPattern此書中策略模式的定義:
定義了演算法家族,個別封裝起來,讓他們之間可以互相替換,此模式讓演算法的變動獨立於使用操作的物件之外
於 HeadFirstDesignPattern 此書中策略模式的原則:
將演算法中可能的變化獨立出來
針對介面寫程式而非針對實踐去編程
多用合成少用繼承
接下來基於以上的定義原則去改寫這個遊戲,從以上範例來看我們可以得出目前有以下行為:
加速
減速
使用道具
攻擊
我們將開始針對這些動作進行封裝,怎麼說呢?加速可能還有分緩慢加速、快速加速對吧? 減速亦然,使用氮氣加速我則把他歸類成道具的使用行為而不再限定只是單一種類,攻擊的話也可能會使用不同種武器的攻擊方式(拿槍or拿刀)。
以上種種的行為都有可能會有許多種的變化,我們當然不可能每增加一種變化就去增加一種方法的方式去實作,如這樣搞就會讓系統變的複雜難以維護。
接下來讓我們看看套用了策略模式後將產生什麼樣的類別圖,如下:
首先說明一下這張類別圖的關係,我將以上所提到的四種行為分別透過介面加以串接。
以Atk來說明,PorscheCar與Atk()原本關係是PorscheCar類別去呼叫父類別或自行實作該Atk()方法,也就是ProscheCar類別必須需要知道我的Atk方法是從哪兒來及要些做什麼。
但是經過修改後透過 IAtkMode介面將PorscheCar與GunMode兩者切開了,如此ProscheCar實際上並不用管我到底要用什麼樣的攻擊方式,而是接收由外部傳入攻擊方式物件後再透過Atk()的呼叫去攻擊,這下就把PorscheCar與GunMode攻擊方式的相依分離了,進而降低了PorscheCar類別與其他類別的耦合度。
痾......
PorscheCar類別修改後宣告全域的四種行為介面之後用來存放經由建構子傳入的行為物件:
publicclassPorscheCar:Car
{
privateISpeedUpSet_speedUp;
privateISpeedDownSet_speedDown;
privateIItemSet_item;
privateIAtkMode_atkMode;
}
PorscheCar類別建構子將接受由外部傳入的行為物件,透過使用介面型別接收
publicPorscheCar(ISpeedUpSetspeedUp,ISpeedDownSetspeedDown,IItemSetitem,IAtkModeatkMode)
{
this._speedUp=speedUp;
this._speedDown=speedDown;
this._item=item;
this._atkMode=atkMode;
}
實際的執行交由傳入的介面方法去執行
publicvoidSpeedUp()
{
this._speedUp.SpeedUp();
}
publicvoidSpeedDown()
{
this._speedDown.SpeedDown();
}
publicvoidUseItem()
{
this._item.UseItem();
}
publicvoidAtk()
{
this._atkMode.Atk();
}
如此即可達成不用管這些方法到底要做什麼,行為的實作由實作該行為介面的類別去做而PorscheCar只需要知道攻擊就是Atk()管你怎麼實作方法的內容,這樣就切開兩個類別的相依了
最後實際兩種方式使用上的差異就如同以下程式碼:
staticvoidMain(string[]args)
{
D1BeforeClass.PorscheCarbCar=newD1BeforeClass.PorscheCar();
bCar.SpeedUp();
bCar.SpeedDown();
bCar.UseNOS();
bCar.Atk();
Console.WriteLine("----套用策略模式後----");
D1AfterClass.HighSpeedhighSpeed=newD1AfterClass.HighSpeed();
D1AfterClass.LowSpeedlowSpeed=newD1AfterClass.LowSpeed();
D1AfterClass.NOSnos=newD1AfterClass.NOS();
D1AfterClass.GunModeweapon=newD1AfterClass.GunMode();
D1AfterClass.PorscheCaraCar=newD1AfterClass.PorscheCar(highSpeed,lowSpeed,nos,weapon);
aCar.SpeedUp();
aCar.SpeedDown();
aCar.UseItem();
aCar.Atk();
Console.Read();
}
在使用策略模式的情況下,如果需要更改移動速度與攻擊的攻擊方式,只需要產生新的速度類別與攻擊方式類別後再將傳入參數替換,如此就能夠動態的改名物件行為卻不用去更改原本主體的程式碼嘍,如下:
增加
publicclassSuperHighSpeed:ISpeedUpSet
{
publicvoidSpeedUp()
{
Console.WriteLine("超級高速的移動中");
}
}
修改
//D1AfterClass.HighSpeedhighSpeed=newD1AfterClass.HighSpeed();
D1AfterClass.SuperHighSpeedhighSpeed=newD1AfterClass.SuperHighSpeed();//替換新物件
D1AfterClass.LowSpeedlowSpeed=newD1AfterClass.LowSpeed();
D1AfterClass.NOSnos=newD1AfterClass.NOS();
//D1AfterClass.GunModeweapon=newD1AfterClass.GunMode();
D1AfterClass.KnifeModeweapon=newD1AfterClass.KnifeMode();//替換新物件
以上就是第一篇策略模式的說明,在下第一次嘗試這種主題,如有錯誤的地方請包涵指教,謝謝。
範例下載
DP_01.rar
參考資料
HeadFirstDesignPattern深入淺出設計模式
Astah繪UML軟體
以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您:)
DesignPattern
回首頁
延伸文章資訊
- 1設計模式學習筆記- Study Design Pattern In Java
... 模式的聖經Design Patterns : Elements of Re-usable Object-Oriented Software,發現這本書很難, 只好先放棄,真正開始是從大話...
- 2一起幫忙解決難題,拯救IT 人的一天
Learning Design Pattern in 30 real-case practices 系列第1 篇 ... 買(借)本好書; 初步了解設計模式(先有概念); 模仿及練習書上範例; ...
- 3隨手Design Pattern (3) - 簡單工廠模式(Simple Factory Pattern)
參考範例
- 4[Design Pattern-Python]設計模式-前言. 經驗分享 - Medium
“[Design Patterns-Python]設計模式-前言” is published by Daniel ... 自己實作過這些設計模式,我只要把模式的概念以及程式的範例解釋就能成一 ...
- 5Chapter3: 策略模式(Strategy Pattern)
1.Strategy Pattern. 2.Strategy Pattern Example ... 若使用策略模式來套用上述範例, 則可以解決繼承所帶來的問題, 也可以重複使用已經寫好的程式碼:.