【設計模式】HeadFirst設計模式(一):策略(Strategy)模式
1、概述
在軟體開發的過程中,實現一個功能可能會使用很多種演算法,一種比較常用的做法是把這些演算法或者策略寫在一個類中,一個演算法寫一個方法。當我們需要新增一個演算法的時候,我們需要修改封裝這些演算法的類,並且在客戶端也可能需要修改相應的實現。如果一個專案需要很多的演算法的情況下,這樣的設計會增加專案的維護複雜度。
2、問題
如何讓演算法獨立於使用它的客戶而獨立變化
3、定義
策略模式:定義了演算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶
4、設計原則
把一個類中經常改變或者將來可能改變的部分提取出來,作為一個介面(也可以是抽象類,java中的abstract),然後在
5、適用環境
因為策略模式主要針對一族演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響 到客戶端的情況下發生變化。通常,策略模式適用於當一個應用程式需要實現一種特定的服務或者功能,而且該程式有多種實現方式時使用。
6、結構
三個物件:
(1) 環境物件: 持有抽象策略物件的引用,供給客戶端直接呼叫
(2)抽象策略物件:策略類,由介面或者抽象類實現
(3)具體策略物件:包裝了相關演算法和行為
UML類圖:
7、應用場景——模擬鴨子
背景: Joe上班的公司做了一套鴨子游戲,遊戲中有各種的鴨子:一邊游泳,一邊呱呱叫。設計了一個鴨子超類,讓各種鴨子繼承此超類
Duck抽象類:quack()方法(已經實現),swim()方法(已經實現),display()方法是抽象方法
MallardDuck類:display()方法描述“鴨子是綠頭”
RedheadDuck類:display()方法描述“鴨子是紅頭”
問題一:老闆決定開發一種能夠飛的鴨子出來,把競爭者拋在後頭。需要怎麼設計?
這是很簡單的事情嘛,只需要在 Duck 超類中增加一個 fly() 方法就可以了。這個時候問題出來了,所有的鴨子都繼承了 fly() 方法,導致了橡皮鴨子在天上飛(橡皮鴨子不會飛)
所以,我們要在橡皮鴨子類(RubberDuck)中把 fly() 方法覆蓋了,讓什麼事情都不做,並且把 quack() 方法覆蓋了,讓“鴨子呱呱叫” 變成 “鴨子吱吱叫”
我們看到新增加了一個類 RubberDuck 橡皮鴨子類:
quack()方法:覆蓋成吱吱叫
display()方法:外觀是橡皮鴨
fly()方法:覆蓋,變成什麼事情都不做
問題二:如果,我們要新增加一個 誘餌鴨DecoyDuck 類(不會飛也不會叫)該怎麼辦?
按照上邊繼承的做法:把quack()方法覆蓋成什麼也不做,把fly()方法覆蓋成什麼也不做,把display()方法實現為:外觀為誘餌鴨
問題三:主管說,公司每過6個月要更新一次產品。Joe知道規格會經常改變,每當有新的鴨子子類出現的時候,他就要被迫檢查並可能需要覆蓋fly()和quack()方法。由此可見,繼承並不是答案,Joe需要怎麼辦呢?
Joe想到了如下的解決方案:
Flyable介面:放一個fly()方法
Quackable介面:放入quack()方法
Duck抽象類(或介面):放入display()和swim()方法,因為所有鴨子都會這兩個方法
MallardDuck類:綠頭鴨子,display()表現為綠頭,fly()表現為會飛,quack()表現為呱呱叫
RedheadDuck類:紅頭鴨子,display()表現為紅頭,fly()表現為會飛,quack()表現為呱呱叫
RubberDuck類:橡皮鴨子,display()表現為橡皮鴨,quack()表現為 吱吱叫 (因為不會飛所以沒有實現Flyable介面)
DecoyDuck類:誘餌鴨, display()表現為誘餌鴨 (因為不會飛不會叫所以不實現兩個介面)
問題三:這個主意真的太笨了,如果這樣設計的話重複的程式碼會變多。如果48個Duck的子類都要稍微修改一下飛行的行為,那麼在48只鴨子類中的fly()方法都要實現
設計原則:把應用中可能需要變化的地方獨立出來,不要和不需要變化的程式碼混合在一起(如果每次有新的需求,都會使某方面程式碼發生變化,那麼久可以確定這部分程式碼需要被抽取出來)
接下來,我們就要設計鴨子了
(1) 分開變化的和不會變化的部分
變化的部分:鴨子的飛行行為和叫的行為
不變的部分:鴨子的描述
所以,我們可以得到下邊的UML圖:
下邊我們看一下具體的實現:
FlyBehavior介面
public interface FlyBehavior {
void fly();
}
飛行類FlyWithWings.java 實現FlyBehavior
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!");
}
}
飛行類FlyNoWay.java實現FlyBehavior
public class FlyNoWay implements FlyBehavior{
public void fly(){
System.out.println("I can't fly.");
}
}
QuackBehavior介面
public interface QuackBehavior {
void quack();
}
Quack類,實現QuackBehavior
public class Quack implements QuackBehavior{
public void quack(){
System.out.println("quack");
}
}
Squeak類,實現QuackBehavior
public class Squeak implements QuackBehavior{
public void quack(){
System.out.println("Squeak");
}
}
MuteQuack類,實現QuackBehavior
public class MuteQuack implements QuackBehavior{
public void quack(){
System.out.println("<<Silence>>");
}
}
Duck抽象類:
public abstract class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void swim(){
System.out.println("All ducks float, even decoys!");
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
我們建立一個DecoyDuck誘餌鴨類(不會飛不會叫)
public class DecoyDuck extends Duck {
//構造方法,初始化確定誘餌鴨不會飛不會叫
public DecoyDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
}
//描述方法,得自Duck類
public void display() {
System.out.println("I'm a decoy duck.");
}
//改變鴨子行為:會飛會叫
public void changeBehavior() {
setFlyBehavior(new FlyWithWings());
setQuackBehavior(new Quack());
}
}