策略模式
場(chǎng)景
設(shè)計(jì)鴨子模擬器系統(tǒng),實(shí)現(xiàn)具有各種行為組合的鴨子
- 剛開始設(shè)計(jì)時(shí),此系統(tǒng)設(shè)計(jì)了標(biāo)準(zhǔn)的OO技術(shù),設(shè)計(jì)了一個(gè)鴨子超類,并讓各種鴨子繼承此超類
問題引入 : "讓鴨子會(huì)飛! 此程序需要會(huì)飛的鴨子(火箭噴射鴨)" "鴨子的叫聲不同(橡皮鴨, 模型鴨)"
較差的實(shí)現(xiàn)方式
-
在超類中增加fly()方法,并給予實(shí)現(xiàn).
- image
所有的鴨子都將擁有飛行的能力,但是并不是所有的鴨子都能飛(橡皮鴨)
解決: 讓不會(huì)飛的鴨子重寫fly方法,什么也不做
思考: 如果系統(tǒng)后續(xù)有各種各樣的行為要增加(假設(shè)有50個(gè)), 并且每種行為有多種實(shí)現(xiàn),并且一個(gè)鴨子可能有各種行為的組合 , 那么不具備這些能力的鴨子都要進(jìn)行重寫,這是一個(gè)好的設(shè)計(jì)嗎?
-
將飛行行為定義成接口,讓鴨子子類去實(shí)現(xiàn)
- image
- 假設(shè)現(xiàn)在有100種鴨子,定義一個(gè)飛行的接口, 需要讓有飛行能力的鴨子全部進(jìn)行重寫, 這樣一來代碼會(huì)重復(fù)很多,這是一個(gè)差勁的設(shè)計(jì).
如何解決
- 問題出現(xiàn)在哪里?
繼承的問題:對(duì)類的局部改動(dòng)机隙,尤其超類的局部改動(dòng)喷屋,會(huì)影響所有子類部分吼虎。影響會(huì)有溢出效果
超類挖的一個(gè)坑站故,每個(gè)子類都要來填,增加工作量晨汹,復(fù)雜度O(N^2) (增加N個(gè)行為,每個(gè)行為N個(gè)類來改)汽畴。不是好的設(shè)計(jì)方式
-
如何設(shè)計(jì)
-
分離可變部分和不可變部分
- 不經(jīng)常變動(dòng)部分: Duck類
- 可變部分: 可能新增的各種行為 fly() , quack()
- 這次鴨子類不會(huì)負(fù)責(zé)實(shí)現(xiàn)Flying和Quacking接口,反而是由我們
制造一組其他類專門實(shí)現(xiàn)FlyingBehavior和QuackBehavior接口, 這稱為"行為類"
, 由行為類而不是Duck類來實(shí)現(xiàn)行為接口 - 關(guān)鍵在于,鴨子現(xiàn)在會(huì)將飛行和呱呱叫的動(dòng)作"委托"(delegate) 別人代理,而不是使用定義在Duck類(或子類)內(nèi)的呱呱叫和飛行方法
-
類圖設(shè)計(jì)
- 重構(gòu)后的分析
- 鴨子需要什么行為組合都能自行決定
- 可以在運(yùn)行中動(dòng)態(tài)改變自己的行為( setBehavior )
策略模式總結(jié)
定義:策略模式定義了
算法族(行為族)
,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化部分 (鴨子行為) 獨(dú)立于算法的客戶(鴨子)
- 模式的理解
- 角色
- 擁有行為的主體(鴨子)
- 各種不同的行為,每種行為都有各種實(shí)現(xiàn)(飛行 / 叫聲)
- 細(xì)節(jié)
- 主體使用
組合行為
的方式, 讓主體任意的組合需要的行為 - 可以在運(yùn)行中動(dòng)態(tài)改變自己的行為( setBehavior )
- 主體使用
- 角色
核心代碼部分
- 鴨子類
public abstract class Duck {
FlyBehavior flyBehavior; //飛行行為
QuackBehavior quackBehavior; //叫聲行為
public Duck() {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
-
行為接口 (飛行\(zhòng)叫聲)
public interface FlyBehavior { public void fly(); } public interface QuackBehavior { public void quack(); }
-
實(shí)現(xiàn)行為接口
//不會(huì)飛行 public class FlyNoWay implements FlyBehavior { public void fly() { System.out.println("I can't fly"); } } //呱呱叫 public class Quack implements QuackBehavior { public void quack() { System.out.println("Quack"); } }
-
具體的鴨子
//模型鴨, 不會(huì)飛,只會(huì)呱呱叫 public class ModelDuck extends Duck { public ModelDuck() { flyBehavior = new FlyNoWay(); quackBehavior = new Quack(); } public void display() { System.out.println("I'm a model duck"); } }
-
鴨子模擬器(主程序)
public class MiniDuckSimulator { public static void main(String[] args) { MallardDuck mallard = new MallardDuck(); RubberDuck rubberDuckie = new RubberDuck(); DecoyDuck decoy = new DecoyDuck(); mallard.performQuack(); rubberDuckie.performQuack(); decoy.performQuack(); Duck model = new ModelDuck(); model.performFly(); //運(yùn)行中改變行為 model.setFlyBehavior(new FlyRocketPowered()); model.performFly(); } }
-
輸出結(jié)果
Quack Squeak << Silence >> I can't fly I'm flying with a rocket
參考
? 書籍: HeadFirst設(shè)計(jì)模式
? 代碼參考地址: 我就是那個(gè)地址