1. 程式人生 > >設計模式 c++版(13)——策略模式

設計模式 c++版(13)——策略模式

定義: 定義一組演算法,將每個演算法都封裝起來,並且使他們之間可以互換

示例一:策略模式(通用版)

1. 類圖18-3

2. 類圖說明

策略模式使用的就是面向物件的繼承和多型機制

  • Context 封裝角色。也叫上下文角色,起承上啟下的封裝作用,遮蔽高層模組對策略、演算法的直接訪問,封裝可能存在的變化。
  • Strategy 抽象策略角色。策略、演算法家族的抽象,通常為藉口,定義每個策略或演算法必須具有的方法和屬性。
  • ConcreteStrategy 具體策略角色。實現抽象策略中的操作,該類含有具體的演算法。

3. 程式碼清單18-3

////////////////////////    **********  1. 策略模式(通用版),程式碼清單18-3:***************//


//抽象的策略角色
class Strategy
{
public:
    virtual void    operate() = 0;
};

//具體策略角色
class ConcreteStrategy1: public Strategy
{
public:
    virtual void    operate(){qDebug() << "ConcreteStrategy1";}
};

//吳國太開綠燈
class ConcreteStrategy2: public Strategy
{
public:
    virtual void    operate(){qDebug() << "ConcreteStrategy2";}
};

//封裝角色
class Context
{
public:
    Context(Strategy* strategy)
    {
        this->m_strategy = strategy;
    }
    void    operate()
    {
        this->m_strategy->operate();
    }

private:
    Strategy   *m_strategy;
};

//使用計謀
int main()
{
    Strategy *gy1      = new ConcreteStrategy1();
    Strategy *gy2    = new ConcreteStrategy2();
    
    Context *context = new Context(gy1);
    context->operate();
    
    context = new Context(gy2);
    context->operate();
    
    return 0;
}

4. 程式碼分析

策略模式就是採用了面向物件的繼承和多型機制。在真實的業務環境中,需要看清楚哪個介面是抽象策略介面,哪些是和策略模式沒有任何關係的。

示例二:三國策略

1. 類圖18-1

 

2. 類圖結構

  • IStrategy :策略類總稱
  • BackDoor:喬國老開後門
  • GivenGreenLight:吳國太開綠燈
  • BlockEnemy:孫夫人斷後

3.增加錦囊和執行人,類圖18-2

4.程式碼清單  



//////////////////////    **********  2. 三國策略,程式碼清單18-2:***************//

//妙計介面
class IStrategy
{
public:
    virtual void    operate() = 0;
};

//喬國老開後門
class BackDoor: public IStrategy
{
public:
    virtual void    operate(){qDebug() << "BackDoor";}
};

//吳國太開綠燈
class GivenGreenLight: public IStrategy
{
public:
    virtual void    operate(){qDebug() << "GivenGreenLight";}
};

//孫夫人斷後
class BlockEnemy: public IStrategy
{
public:
    virtual void    operate(){qDebug() << "BlockEnemy";}
};

//錦囊
class Context
{
public:
    Context(IStrategy* strategy)
    {
        this->m_strategy = strategy;
    }
    void    operate()
    {
        this->m_strategy->operate();
    }

private:
    IStrategy   *m_strategy;
};

//使用計謀
int main()
{
    IStrategy *backDoor      = new BackDoor();
    IStrategy *greenLight    = new GivenGreenLight();
    IStrategy *block         = new BlockEnemy();
    
    
    Context *context = new Context(backDoor);
    context->operate();
    
    context = new Context(greenLight);
    context->operate();
    
    context = new Context(block);
    context->operate();
    
    return 0;
}

三、建造者模式的應用

1. 優點

  •  演算法可以自由切換。這是策略模式本身定義的,只要實現抽象策略,它就成為策略家族的一個成員,通過封裝角色對其進行封裝,保證對外提供“可自由切換”的策略。
  •  避免使用多重條件判斷。多重條件語句不易維護,而且出錯的概率大大增強。使用策略模式後,可以由其他模組決定採用何種策略,策略家族對外提供的訪問介面就是封裝類,簡化了操作,同時避免了條件語句判斷。
  •  擴充套件性良好。在現有的系統中增加一個策略很容易,只要實現介面就可以了,其他不用修改,類似於一個可反覆拆卸的外掛。

2. 缺點

  •  策略類數量增多。每一個策略都是一個類,複用的可能性很小,類數量增多。
  •  所有的策略類都需要對外暴露。上層模組必須知道有哪些策略,然後才能決定使用哪一個策略,這與迪米特法則相違背,這一缺點可以使用其他模式來修補,如工廠方法模式、代理模式或享元模式。

3. 使用場景:

  •  多個類只有在演算法或行為上稍有不同的場景。
  •  演算法需要自由切換的場景。如演算法的選擇由使用者決定。
  •  需要遮蔽演算法規則的場景。如太多的演算法只要知道一個名字,傳遞相關的數字進來,反饋一個運算結果就可以。

4. 注意事項:

如果系統中的一個策略家族的具體策略數量超過4個,則需要考慮使用混合模式,解決策略類膨脹和對外暴露的問題,否則日後的系統維護就會很麻煩。

四、策略模式的擴充套件

1. 需求說明

輸入3個引數,進行加減運算,引數中兩個是int,一個是QString,只有“+”、“-”兩個符號可以選擇,不考慮校驗

示例三:算數加減法(方案一)

2. 程式碼清單18-3  

////////////////////////    **********  3. 算數加減法(方案一),程式碼清單18-3:***************//

class Calculator
{
public:
    int exec(int a, int b, QString symbol)
    {
        int result = 0;
        if (symbol == this->m_add)
        {
            result = this->add(a, b);
        }
        else if (symbol == this->m_sub)
        {
            result = this->sub(a, b);
        }
        return result;
    }
private:
    int     add(int a, int b){return a + b;}
    int     sub(int a, int b){return a - b;}
    
private:
    static QString  m_add;
    static QString  m_sub;
};

QString  Calculator::m_add = "+";
QString  Calculator::m_sub = "-";

int main()
{
    int a = 10;
    int b = 20;
    Calculator cal;
    qDebug() << cal.exec(a, b, "+");
    
    
    return 0;
}

示例四:算數加減法(方案二)

2. 程式碼清單18-3,( 簡化Calculator )

////////////////////////    **********  4. 算數加減法(方案二),程式碼清單18-4:***************//


class Calculator
{
public:
    int exec(int a, int b, QString symbol)
    {
        return (symbol == m_add)? a+b:a-b; 
    }
private:
    int     add(int a, int b){return a + b;}
    int     sub(int a, int b){return a - b;}
    
private:
    static QString  m_add;
    static QString  m_sub;
};

QString  Calculator::m_add = "+";
QString  Calculator::m_sub = "-";

int main()
{
    int a = 10;
    int b = 20;
    Calculator cal;
    qDebug() << cal.exec(a, b, "+");
    
    
    return 0;
}

示例五:算數加減法(方案三)

2. 程式碼清單18-3,( 引入策略模式 )  


//////////////    **********  5. 算數加減法(方案三:策略模式),程式碼清單18-5:***************//


class Calculator
{
public:
    virtual int exec(int a, int b) = 0;
};

class Add:public Calculator
{
public:
    virtual int exec(int a, int b)
    {
        return a + b;
    }
};

class Sub:public Calculator
{
public:
    virtual int exec(int a, int b)
    {
        return a - b;
    }
};

class Context
{
public:
    Context(Calculator  *cal)
    {
        this->m_cal = cal;
    }
    int exec(int a, int b)
    {
        return this->m_cal->exec(a, b);
    }

private:
    Calculator  *m_cal;
};

int main()
{
    int a = 10;
    int b = 20;
    Calculator *cal = new Add();
    Context context(cal);
    qDebug() << context.exec(a, b);
     
    return 0;
}

六、最佳實踐

策略模式是一個非常簡單的模式。他在專案中使用的非常多,但單獨使用的地方比較少,因為有致命缺陷:所有的策略都需要暴露出去,這樣才方便客戶端決定使用哪一個策略。

參考文獻《秦小波. 設計模式之禪》(第2版) (華章原創精品) 機械工業出版社