【模式設計】04 Strategy 策略模式(“組件協作”模式二)

當我們在code的過程中出現了用if/else if,除非那種絕對的比如(if man; else woman)(別較真說除了男女還有其他的),除了這種絕對的情況存在,其他情況我們都應該考慮到Strategy模式。遵循面向對象設計原則中的開放閉合封閉原則。(面向對象我們應該考慮抽象的思想,而不是考慮分而治之的思想)

Strategy 策略模式

01 動機(Motivation)

在軟件構建過程中,某些對象使用的算法可能多種多樣,經常改變,如果將這些算法都編碼到對象中,將會使對象變得異常複雜;而且有時候支持不使用的算法也是一個性能負擔。

如何在運行時根據需要透明地更改對象的算法?將算法與對象本身解耦,從而避免上述問題?

02 模式定義

定義一系列算法,把它們一個個封裝起來,並且使它們可互相替換(變化)。該模式使得算法可獨立於使用它的客戶程序(穩定)而變化(擴展,子類化)。
——《設計模式》GoF

03 結構(Structure)

在這裏插入圖片描述
紅色代表穩定的部分,而藍色圈的代表變化的部分。(可以在下面僞代碼 06得到很好的理解)

04 舉例:

稅種的計算,到哪個國家,都有哪個國家的稅種計算。

僞代碼 05

enum TaxBase {
	CN_Tax,
	US_Tax,
	DE_Tax,
	FR_Tax       //更改  //違背開閉原則
};
/*稅種的計算*/
class SalesOrder{
    TaxBase tax;
	public:
	    double CalculateTax(){
	        //...
	        
	        //這是結構性流程,是一種分而治之的思想。
	        if (tax == CN_Tax){
	            //CN***********
	        }
	        else if (tax == US_Tax){
	            //US***********
	        }
	        else if (tax == DE_Tax){
	            //DE***********
	        }
			else if (tax == FR_Tax){  //更改 //違背開閉原則
				//...
			}
	
	        //....
	     }
    
};

如上代碼,假如我們業務擴展了,需要加入法國的稅種,那麼此時我們需要對源碼進行更改,違背開閉原則,其次,假如我們就在法國,那我們每次運行代碼都會對其他國家進行一個判斷,浪費內存。
(不具有代碼的複用性,具有代碼的複製性,哈哈曾經我以爲複製性就是複用性。)

僞代碼 06

class TaxStrategy{
public:
    virtual double Calculate(const Context& context)=0;
    virtual ~TaxStrategy(){};//一個虛的構造函數,不管本身默認的析構函數夠不夠用,都應該寫一個虛的析構函數
};

//標準軟件開發,應該不同類放在不同文件中
class CNTax : public TaxStrategy{
	public:
	    virtual double Calculate(const Context& context){
	        //***********
	    }
};

class USTax : public TaxStrategy{
	public:
	    virtual double Calculate(const Context& context){
	        //***********
	    }
};

class DETax : public TaxStrategy{
	public:
	    virtual double Calculate(const Context& context){
	        //***********
	    }
};
//擴展
//*********************************
class FRTax : public TaxStrategy{
	public:
		virtual double Calculate(const Context& context){
			//.........
		}
};


class SalesOrder{
private:

	//這塊常用到後面的工廠模式
    TaxStrategy* strategy;//指針支持指向基類的不同對象 ,或者很少放一個引用。常用指針。

public:
    SalesOrder(StrategyFactory* strategyFactory){
        this->strategy = strategyFactory->NewStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }

    public double CalculateTax(){
        //...
        Context context();
        
        double val = 
            strategy->Calculate(context); //多態調用
        //...
    }
    
};

//代碼具有良好的本地性

如上代碼:假如我們擴展業務假如法國的稅種,那麼我們只需要再加上法國稅種的類就好了,也不需要對源代碼進行更改,代碼具有良好的複用性。其次假如我們的業務範圍只有中國,那我們每次就僅僅只在中國稅種的類裏面進行判斷調用,增加了代碼的性能。
(簡單說明一下:複用性不是代碼的複製性,不是說一個代碼可以進行多次複製粘貼多次使用,而說的是在二進制層面上,編譯之後,不再進行改動,這纔是複用性)

反正話說回來:就是希望我們通過對代碼進行擴展而達到需求的擴展,而不是對代碼底層進行修改。

05 要點總結

Strategy及其子類爲組件提供了一系列可重用的算法,從而可以使得類型在運行時方便地根據需要在各個算法之間進行切換。

Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要Strategy模式。

如果Strategy對象沒有實例變量,那麼各個上下文可以共享同一個Strategy對象,從而節省對象開銷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章