我們如何看待設計模式?
設計模式需要站在時間軸上來看待,需要看到未來。如果一個系統是靜態的,那我們也不需要設計模式,畢竟引入了某種設計模式之後,系統的複雜性以及可理解性就會大打折扣。
策略模式
在軟件設計的過程中,某些對象使用的算法可能多種多樣,經常會發生改變,如果將這些算法都硬編碼到對象中,通過 if…else… 或者 switch…case… 來進行管理,將會使對象變得臃腫。
策略模式要解決的問題就是如何在運行時根據需要透明地更改對象所使用的算法?將對象與算法解耦,從而避免上述問題。
場景模擬
例如我們有一個零售銷售品工單的場景,工單裏面需要計算稅收,於是就存在按照不同國家稅收政策進行稅收計算的工單。
一開始我們用枚舉來標識不同國家的稅務類型,用switch…case… 來進入不同的算法分支進行稅務計算,代碼如下。
#ifndef _SALES_ORDER_H_
#define _SALES_ORDER_H_
enum TaxType{
CN_TAX,
US_TAX,
UK_TAX
//....如果增加稅收品種就在這裏擴展
};
class SalesOrder{
public:
SalesOrder():m_iTaxTyoe(-1){}
SalesOrder(TaxType taxtype):m_iTaxTyoe(taxtype){}
void setTaxTye(TaxType taxtype)//設置稅收政策
{
m_iTaxTyoe = taxtype;
}
void calculateTax()
{
switch (m_iTaxTyoe)
{
case CN_TAX:
/* 中國稅務算法 */
break;
case US_TAX:
/* 美國稅務算法 */
break;
case UK_TAX:
/* 英國稅務算法 */
break;
//如果稅收種類增加,則在這裏增加case...
default:
/*未定義*/
break;
}
}
//....
private:
int m_iTaxTyoe;
};
#endif
實際上,如果我們業務是靜態的不需要再進行業務擴展變化的話,這樣寫也就足夠了。
但實際上公司要發展,業務要拓展,後面我們就可能會引入德國的稅收算法、日本的稅收算法等等。那時候如果我們需要擴展我們的代碼的話,就需要在枚舉下面新增,以及我們的**switch…case…**下面新增對應case。
我們梳理一下思路,上述場景變化的地方在於稅收算法,我們工單類與算法存在依賴關係。於是我們可以把稅率算法提取出來一個稅率算法抽象類,讓工單類依賴於這個稅率算法抽象類。UML圖大致如下:
代碼實現(C++)
工單類:
/*filename:SalesOrder.h*/
#ifndef _SALES_ORDER_H_
#define _SALES_ORDER_H_
#include "ITaxalgorithm.h"
class TaxContext{};//稅率計算上下文
class SalesOrder
{
public:
SalesOrder(ITaxalgorithm* pTax = nullptr):m_pTax(pTax){}
~SalesOrder(){}
void setTaxType(ITaxalgorithm* pTax)
{
m_pTax = pTax;
}
void calculateTax()
{
TaxContext context;
if(m_pTax)
{
m_pTax->calculate(context);//多態調用
}
}
private:
ITaxalgorithm* m_pTax;//稅率算法基類指針
};
#endif
稅率算法抽象類:
/*filename:ITaxalgorithm.h*/
#ifndef _ITAXALGORITHM_H_
#define _ITAXALGORITHM_H_
class TaxContext;//稅率計算上下文
class ITaxalgorithm{
public:
virtual void calculate(const TaxContext & context);
virtual ~ITaxalgorithm();
};
#endif
具體算法類:
/*filename:CNTax.h*/
#ifndef _ITAXALGORITHM_H_
#define _ITAXALGORITHM_H_
class TaxContext;//稅率計算上下文
class ITaxalgorithm{
public:
virtual void calculate(const TaxContext & context);
virtual ~ITaxalgorithm();
};
#endif
使用,構造一個以中國稅率算法計算的工單:
/*filename:main.cc*/
#include "CNTax.h"
#include "SalesOrder.h"
int main()
{
CNTax cntax;
SalesOrder order;
order.setTaxType(&cntax);
order.calculateTax();
return 0;
}
總結:
可以看出,設計模式主要還是抓住系統中的變與不變,以時間軸來看,把將來可能會變動的地方剝離出來,提取出一個抽象類,讓其他組件來依賴這個抽象類而不是具體的某個實現,從而達到模塊解耦的目的。
注意:
我們這裏說的依賴若不做特殊說明皆是指編譯時依賴,而非運行時依賴。