第4節 策略模式

一、 策略模式動機

  1. 軟件構建中,某些對象算法可能多種多樣,經常改變,如果將它們都編碼到對象中,會使得對象非常複雜,有時支持不適用的算法也會造成性能負擔;
  2. 如何在運行時透明地更改對象的算法,將算法與對象本身解耦,從而避免上述問題?

二、策略模式定義( GOF定義)

定義一系列算法,把他們一個個封裝起來,並且使他們可互相替換(變化)。該模式使得算法可獨立於使用他們的客戶程序(穩定)而變化.

三、代碼示例

3.1 重構前代碼

#include <stdio.h>

enum TaxBase           // 稅種的枚舉體
{
    CN_Tax = 0,
    US_Tax = 1,
    DE_Tax = 2
};

class SalesOrder
{
public:
    SalesOrder(TaxBase tax) { this->tax = tax; }
    ~SalesOrder() {}
public:
    double CalculateTax()
    {   // 根據稅種類不同,計算稅額,不同國家算法不同
	if (tax == CN_Tax) {}       // 情況1...
	else if (tax == US_Tax) {}  // 情況2...
	else if (tax == DE_Tax) {}  // 情況3...
	return 0;
    }
private:
    TaxBase tax;
};

int main()
{
    SalesOrder sales_order(TaxBase::CN_Tax);
    sales_order.CalculateTax();
    renturn 0;
}

3.2 問題思考

程序咋一看沒什麼問題,但是否考慮到程序的後續可擴展性?比如增加一些國家(枚舉體),也會增加相應的算法;
違背原則:多擴展開放、對修改封閉. 本示例代碼中,如將來再實現其它國家稅率計算的擴展,doubleCalculateTax()的代碼就需要跟着相應變化.

3.3 重構後代碼

#include <stdio.h>

// 定義基類,抽象稅率計算統一的格式
class TaxStrategy {
public:
    virtual double CalculateTax(const Context& contex) = 0;  // 純虛方法
    virtual ~TaxStrategy() {}                                // 良好的習慣,基類析構都寫成虛函數
};

// 添加擴展1:中國稅率計算類
class CNTax :public TaxStrategy {
public:
    virtual double CalculateTax(const Context& contex) {     // 實現接口類的純虛函數
	// Override:實現China的算法
	return 0;
    }
};

// 添加擴展2:美國稅率計算類
class USTax :public TaxStrategy {
public:
    virtual double CalculateTax(const Context& contex) {     // 實現接口類的純虛函數
	//  Override:實現US的算法 
	return 0;
    }
};

// 添加擴展3:德國稅率計算類
class DETax :public TaxStrategy {
public:
    virtual double CalculateTax(const Context& contex) {     // 實現接口類的純虛函數
	//  Override:實現DE的算法
	return 0;
    }
};

// 這裏,還可以添加任何擴展

// 即使增加了國家,SalesOrder類也不需要發生任何變化
class SalesOrder
{
public:
    SalesOrder(StrategyFactory* strategyFactory){
		this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder() { delete this->strategy; }
public:
    double CalculateTax()
    {
	Context contex;
	double val = strategy->CalculateTax(contex);
	return val;
    }
private:
    TaxStrategy* strategy;          // 稅率策略,這是一個多態指針,如果放一個對象就沒有多態性了
};

// 工廠類模式將再後面的章節講解,這裏暫時先認爲它是一個產生抽象對象的工具
class StrategyFactory
{
public:
    StrategyFactory()  {}
    ~StrategyFactory() {}
public:
    TaxStrategy* NewStrategy() {
	// 後面講解工廠模式的時候再添加,這裏是策略模式的重點,怎麼在二進制級別複用,如不停機擴展
    }
};

// 真正的複用指的是二進制意義的複用,而不是源代碼級別的複用
int main()
{
    StrategyFactory* strategyFactory;
    SalesOrder2 sales_order2(strategyFactory);        
    sales_order2.CalculateTax();

    return 0;
}

四、策略模式要點總結

  1. Strategy及子類爲組件提供了一些列可重用的算法,從而使得類型在運行時方便根據需要在各算法之間切換;
  2. 策略模式提供了用條件判斷語句以外的一種可擴展的選擇,消除條件語句的耦合性,含有多個條件分支的代碼通常都需要策略模式;
  3. 如果策略對象沒有實例變量,那麼各個上下文可共享一個策略對象,從而節省對象開銷.

五、 思路總結

  1. 使用策略模式重構if語句的情形,在於if的情形始終存在變化的情形;
  2. 如果if分支的情況永遠不變,則if分支即可; Case1
  3. 如果if分支不變,但在某些情形下某些分支很大概率不會被執行,這是可能造成性能負擔,這是也可以使用策略模式. Case2
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章