設計模式之Strategy(策略模式)

一、動機與意圖

動機:在軟件構建的過程當中,某些對象使用的算法可能多種多樣,常常改變,若是將這些算法都編碼到對象中,將會使對象變得異常複雜,增長與改變現有算法可能帶來嚴重的邏輯問題;並且有時候支持不使用的算法也是一個性能上的負擔(具體上體如今代碼太長會使得其在保存的位置出現問題),所以將對象與算法解耦,避免上述問題纔是本算法動機。
意圖:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。本模式使得算法可獨立於使用它的客戶而改變。ios

二、分析

示例代碼:算法

#ifndef STRATEGY
#define STRATEGY
#include<iostream>
using std::cout;
using std::ends;
using std::endl;
class Tax_cal
{
public:
    Tax_cal();
    ~Tax_cal();
    virtual double get_tax(double source_money)const = 0;
private:

};
Tax_cal::Tax_cal()
{}
Tax_cal::~Tax_cal()
{}
class chinese_tax:public Tax_cal
{
public:
    chinese_tax();
    ~chinese_tax();
    double get_tax(double source_money) const
    {
        return source_money * 5.6/100 ;
    }
};
chinese_tax::chinese_tax()
{}
chinese_tax::~chinese_tax()
{}
class Japanese:public Tax_cal
{
public:
    Japanese();
    ~Japanese();
    double get_tax(double source_money)const
    {
        return source_money * 17 / 100;
    }
private:

};

Japanese::Japanese()
{
}

Japanese::~Japanese()
{
}
class bank_calcu 
{
public:
    bank_calcu(double);
    ~bank_calcu() {};
    void Calculate(const Tax_cal*);
private:
    double money;
};
bank_calcu::bank_calcu(double money) :money(money) {};
void  bank_calcu::Calculate(const Tax_cal* tax_demo) 
{
    cout << tax_demo->get_tax(money) << endl;
}
#endif // !STRATEGY

咱們定義了一個客戶端的類bank_calcu用於計算不一樣地方的稅率,在成員函數Calculate()中計算稅率,可是不一樣的地方有不一樣的稅率,譬如說代碼中的中國與日本(固然數據是瞎編的),傳統的方法是在Calculate()內部寫上一個if-else或者switch語句來進行區別,可是依舊存在問題:
一、用不到的代碼塊就應該被去除,不然影響執行效率;
二、違反了開閉原則(對增長開放,對修改封閉);
三、容易出錯,如今的邏輯尚未那麼複雜,判斷邏輯十分複雜的話,修改會形成嚴重的邏輯問題。
稅率計算的過程咱們定義一個抽象類Tax_cal,使用其進行稅率的具體計算,不一樣的計算規則在其子類中進行具體實現,經過這種方式可以有效地實現稅率計算的功能。
運行代碼爲:函數

#include"strategy.h"
int main() 
{
    bank_calcu bc(16000);
    bc.Calculate(new chinese_tax());
    bc.Calculate(new Japanese());
    std::cin.get();
}

結果爲
clipboard.png
Strategy模式的優勢主要爲:
一、相關算法系列
Strategy類層次爲bank_calcu 定義了一系列可供重用的算法與行爲。繼承有助於析取出這些算法中的公共功能。
二、一個代替繼承的方法
在代碼中咱們還能夠這樣設計,也就是給bank_calcu 設計派生類,每一個派生類支持獨有的算法,可是這樣的派生類不只存在大量的代碼重複,也就是未作到有效地複用,也會帶來難以拓展與難以維護的問題。將算法封裝在Strategy類中有利於理解與擴展。
三、消除了一些條件語句
Strategy提供了用條件語句選擇所需的行爲之外的另外一種選擇。
四、實現了選擇
Strategy 模式能夠提供相同的行爲的不一樣實現。
其主要缺點爲:
一、客戶必需要了解不一樣的Strategy
也就是說既然須要選擇合適的Strategy就須要客戶瞭解不一樣的Strategy的區別在哪,這可能須要將策略的具體實現過程暴露給了用戶。
二、Strategy與bank_calcu 的通訊開銷
在本代碼範例中Strategy的具體實現共享一個接口,可是實際狀況下因爲某些差別,入口參數並非全部的派生類成員函數都須要的,可是這種方式可以下降二者之間的耦合度;另外一種方法是將bank_calcu的引用做爲參數傳入,或者直接在Strategy存儲一個bank_calcu引用,這樣會形成必定程度的高耦合。
三、增長了對象的個數
Strategy雖然說消除了bank_calcu 中的代碼臃腫與難以擴展的問題,可是其代價就是須要大量的類。
四、一些小的tip
在進行Strategy的選擇的時候須要傳入一個具體Strategy指針,可是還有其餘的辦法,也就是經過模板,修改後的bank_calcu 能夠經過模板選擇:性能

template<class Tax_cal>
class bank_calcu 
{
public:
    bank_calcu(double);
    ~bank_calcu();
    void Calculate();
private:
    double money;
    Tax_cal* tax_type;//保存一個Abstrategy指針
};
template<class Tax_cal>
bank_calcu< Tax_cal>::bank_calcu(double money) :money(money) 
{
    tax_type = new Tax_cal();//給該指針分配內存
};
template<class Tax_cal>
bank_calcu< Tax_cal>::~bank_calcu() 
{
    delete tax_type;//析構該內存
};
template<class Tax_cal>
void  bank_calcu< Tax_cal>::Calculate()
{
    cout << tax_type->get_tax(money) << endl;
}
#endif // !STRATEGY

調用的過程爲:編碼

#include"strategy.h"
int main() 
{
    bank_calcu<chinese_tax> t1(16000);
    t1.Calculate();
    std::cin.get();
}

這樣調用起來就更加順手了。spa

相關文章
相關標籤/搜索