策略(strategy)模式
Head First一書中對于策略(strategy)模式的正式定義是:策略模式定義了算法族,分別封裝起來,讓他們之間可以相互替換,此模式讓算法的變化獨立于使用算法的客戶。
為了介紹這個算法,書中講了一個例子:
在某個游戲中,有各種各樣的鴨子,系統的內部設計使用了嚴格的OO技術,設計了一個鴨子(Duck)父類,所有的鴨子種類均繼承于此父類。Joe設計的鴨子類如下:
package com.first.strategy;public class Duck1 {public Duck1(){}//鴨子會游泳public void swim(){ }//鴨子會呱呱叫public void quack(){ }//鴨子會飛public void fly(){ }//描述不同的鴨子,子類繼承時覆蓋重寫public void display(){ } }然后讓各種鴨子都繼承此類,并且重寫display()方法,例如:
class DuckA extends Duck1 {public void display(){ System.out.println("I am DuckA!");} }這樣的設計缺點顯而易見:新加入的鴨子種類“橡皮鴨子”也能飛,且也能呱呱叫(橡皮鴨子不能飛也不能呱呱叫),這是違背現實的。因為所有的子類均會繼承來自父類的fly()方法和quack()方法,所有的子類均具備了fly()和quack(),使得不適合子類的行為也繼承了父類的行為。
這時,Joe又想到了繼承,可以在子類橡皮鴨(RubberDuck)中覆蓋父類的fly()和quack()方法,讓其不能飛也不能呱呱叫。
package com.first.strategy;public class RubberDuck extends Duck1{@Overridepublic void quack() {// TODO Auto-generated method stubSystem.out.println("我不會呱呱叫,我會吱吱叫!");}@Overridepublic void fly() {// TODO Auto-generated method stubSystem.out.println("I can not fly!");} }
這時缺點同樣清楚:代碼重復太多,每個子類全部要覆蓋,代碼的可重用性太低。
Joe想到了接口,即把Duck類中的fly和quack從父類中抽象出來,變成接口Flyable和Quackable,這樣,只有會飛的鴨子子類才繼承Flyable接口,只有會呱呱叫的鴨子繼承Quackable接口。這也不是一個好主意,因為它的缺點同樣顯而易見:和上面重寫覆蓋類似,重復的代碼太多,不易維護,要修改,子類全要修改。
既然繼承并不能很好的解決這個鴨子問題,那么我們便尋求變化。鴨子的行為在子類中不斷變化,并且讓所有的鴨子都具有這些行為是不恰當的。Flyable和Quackable接口的想法開始不錯,但是,Java接口中的方法不具有實現,所有子類繼承接口無法達到代碼復用的效果。這就意味著,無論何時要修改某個行為,那么必須向下追蹤所有定了這個行為的類(或子類)中去修改。這時一個設計原則應運而生,恰好解決此問題:
設計原則1:找出應用中可能需要變化之處,把他們獨立出來,不要把需要變化的代碼和不變的代碼混雜在一起。即:把會變化的的部分取出來封裝起來,以便以后可以輕易的改動或擴充此部分,而不影響不會變化的部分。
現在就把變化的部分從Duck類中分離,變化的部分就是fly()和quack(),因為這兩者會隨著鴨子的不同而改變。我們分離出來的兩個方法建立兩個類,一個是與fly相關,一個與quack相關,每一組類實現各自的動作或行為。例如一個類實現“呱呱叫”,另外一個類實現“吱吱叫”,還有一個類實現“安靜不叫”(例如木頭鴨子)。這是運用了第二條原則:
設計原則2:針對接口編程,而不是實現。
所有的和fly相關的行為,組裝成一個接口,quack接口類似。并且分別定義各種與之相關的行為。
package com.first.strategy;public interface FlyBehavior {public void fly();} package com.first.strategy;public interface QuackBehavior {public void quack(); }呱呱叫類:
package com.first.strategy;public class Quack implements QuackBehavior{@Overridepublic void quack() {// TODO Auto-generated method stub//呱呱System.out.println("quack..quack");}}吱吱叫類:
package com.first.strategy;public class Squeak implements QuackBehavior{public void quack() {// TODO Auto-generated method stub//吱吱System.out.println("squeak..squeak");} } 和fly相關的兩個類:<pre class="java" name="code"><pre class="java" name="code">package com.first.strategy;public class FlyNoWay implements FlyBehavior{@Overridepublic void fly() {// TODO Auto-generated method stubSystem.out.println("Can not fly!!");}} package com.first.strategy;public class FlyWithWings implements FlyBehavior{@Overridepublic void fly() {// TODO Auto-generated method stubSystem.out.println("Flying with wings!");}}這樣一來,可以讓飛行和呱呱叫的動作被其他的對象復用,因為這些行為被抽象出來,與鴨子類無關了。我們可以增加一些動作,既不會影響現有的行為類,也不會影響使用這些行為類的鴨子類。
現在要整合鴨子的行為:
1.在Duck中加入兩個實例變量,分別是FlyBehavior的實例變量flybehavior和QuackBehavior的實例變量quackbehavior,為借口類型而不是具體的實現類型(為了使用多態)。同時將原Duck類中的fly()和quack()方法刪除,加入兩個相似的方法performFly()和performQuack()
package com.first.strategy;public abstract class Duck {FlyBehavior flybehavior;QuackBehavior quackbehavior;public Duck(){}public abstract void display();public void performFly(){flybehavior.fly();}public void performQuack(){quackbehavior.quack();}public void swim(){System.out.println("All ducks can swim!");} }2.Duck子類中對flybehavior和quackbehavior實例變量的設置:
package com.first.strategy;public class MallardDuck extends Duck{public MallardDuck(){flybehavior = new FlyWithWings();quackbehavior = new Squeak();}public void display(){System.out.println("I am a mallardDuck!");} } package com.first.strategy;public class RubberDuck extends Duck1{@Overridepublic void quack() {// TODO Auto-generated method stubSystem.out.println("我不會呱呱叫,我會吱吱叫!");}@Overridepublic void fly() {// TODO Auto-generated method stubSystem.out.println("I can not fly!");} } 3.主測試用例package com.first.strategy;public class Client {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubDuck mallardduck = new MallardDuck();mallardduck.display();mallardduck.performFly();mallardduck.performQuack();}}?
4.運行程序。
動態設定行為
1.在鴨子里很多動態的功能沒有用到,很可惜,我們可以在Duck類中增加setter方法來設置鴨子的行為,而不是在構造器中實例化:
package com.first.strategy;public abstract class Duck {FlyBehavior flybehavior;QuackBehavior quackbehavior;public Duck(){}public void setFlyBehavior(FlyBehavior flybehavior){this.flybehavior = flybehavior;}public void setQuackBehavior(QuackBehavior quackbehavior){this.quackbehavior = quackbehavior;}public abstract void display();public void performFly(){flybehavior.fly();}public void performQuack(){quackbehavior.quack();}public void swim(){System.out.println("All ducks can swim!");} }
2.構造一個新的鴨子模型:模型鴨子(ModelDuck),這個鴨子開始不會飛。
3.建立新的FlyBehavior類型
4.主測試類:
?
鴨子和FlyBehavior和QuackBehavior是“HAS—A”關系,兩個類組合起來(composition),鴨子的行為不是繼承來的,而是組合而來。這里是第三個設計原則:
原則3:多用組合,少用繼承。
使用組合組建系統具有很大的彈性,不僅可將算法族封裝成類,更可以在運行時動態的改變行為。
?
轉載于:https://www.cnblogs.com/sunp823/p/5601416.html
總結
以上是生活随笔為你收集整理的策略(strategy)模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: How does a relationa
- 下一篇: 操作系统之进程管理:17、死锁