大話設計模式C++版——表驅動法改造簡單工廠

上回《大話設計模式C++版——簡單工廠模式》中指出了簡單工廠模式的缺陷,即違背了開發—封閉原則,其主要原因是由於switch的判斷結構的使用,使修改或添加新的對象時需要改動簡單工廠類的代碼,如何改造switch結構,表驅動法就可以粉墨登場了。

表驅動法的介紹見《數據驅動編程之表驅動法》。


1、面向接口編程,先改造抽象接口類IOperation

class IOperation
{
public:
	IOperation() : m_nNuml(0), m_nNumr(0) {}
	virtual	~IOperation() {}
	
	virtual	void	SetNum(int nNuml = 0, int nNumr = 0)
	{
		m_nNuml = nNuml;
		m_nNumr = nNumr;
	}

	virtual	int		CalculateResult() = 0;

public:
	typedef	IOperation* (CALLBACK* fn_CreateObject)();

protected:
	int	m_nNuml, m_nNumr;
};
接口基本無改動,由於表驅動法中需要在表中填入驅動函數接口,故先定義一個該接口函數的定義,用於產生具體接口對象。


2、改造接口對象類

class COperation_Add : public IOperation
{
public:
	int		CalculateResult()
	{
		return	m_nNuml + m_nNumr;
	}

	static	IOperation*	CALLBACK CreateObject()
	{
		return	new	COperation_Add();
	}
};


class COperation_Dec : public IOperation
{
public:
	int		CalculateResult()
	{
		return	m_nNuml - m_nNumr;
	}

	static	IOperation*	CALLBACK CreateObject()
	{
		return	new	COperation_Dec();
	}
};
增加了CreateObject()靜態函數,改函數的作用是用來產生本對象。由於表驅動函數是用於回調的,所以需要定義爲靜態回調函數。


3、改造工廠類,增加驅動表改造switch結構(重點)

class CClassFactory
{
public:
	CClassFactory() {}
	virtual ~CClassFactory() {}
	
	BOOL	AddOperationFunc(char cOperation, IOperation::fn_CreateObject func)
	{
		if (func)
		{
			m_mapOperationFunc[cOperation] = func;
			return	TRUE;
		}
		
		return	FALSE;
	}

	IOperation*	CreateObject(char cOperation)
	{
		IOperation::fn_CreateObject	func = m_mapOperationFunc[cOperation];

		if (func)
		{
			return	func();
		}

		return	NULL;
	}

protected:
	std::map<char, IOperation::fn_CreateObject>	m_mapOperationFunc; //用map實現驅動表
};
工廠類中的switch消失了,換成了在map中以操作符爲key找對應驅動函數,然後進行調用。同時增加了添加新操作符和對應驅動函數的接口AddOperationFunc(),這樣如果需要新增加對象或者修改操作符對應的驅動函數,調用此接口即可,靈活性大大增加。


4、使用示例

void	Test()
{
	CClassFactory	oCClassFactory;
	IOperation*		poIOperation = NULL;
	

	//添加對象工廠的生產對象
	oCClassFactory.AddOperationFunc('+', COperation_Add::CreateObject);
	oCClassFactory.AddOperationFunc('-', COperation_Dec::CreateObject);

	poIOperation = oCClassFactory.CreateObject('+');
	
	if (poIOperation)
	{
		poIOperation->SetNum(2, 3);
		printf("2 + 3 = %d\n", poIOperation->CalculateResult());
		
		delete poIOperation;
	}

	poIOperation = oCClassFactory.CreateObject('-');

	if (poIOperation)
	{
		poIOperation->SetNum(2, 3);
		printf("2 + 3 = %d\n", poIOperation->CalculateResult());
		
		delete poIOperation;
	}
}
使用前需添加操作符和對應的驅動函數,這樣一來將用戶的任務加重了,而且需要用戶認識的類增加了,封裝性降低了。是否可進一步改進?


5、進一步改進簡單工廠類,提高封裝性

class CNewClassFactory : CClassFactory
{
public:
	virtual	~CNewClassFactory()	{}

	virtual	void	Init()
	{
		AddOperationFunc('+', COperation_Add::CreateObject);
		AddOperationFunc('-', COperation_Dec::CreateObject);
	}
};

新簡單工廠類中,增加了一個初始化函數Init(),代替用戶的進行操作符和對應的驅動函數,這樣用戶只需要調用Init()函數即可,封裝性進一步提高,那麼有同學可能會問,如果要增加新的計算對象,還不得改動Init()函數代碼,這不跑了一圈又回到沒改造前的原點了。

這就得用到開放—封閉原則了,新簡單工廠類CNewClassFactory中,我們將Init()函數申明爲虛函數了,如果我們要增加新的計算對象,可以有2個途徑:

5.1 用戶在自己代碼中調用AddOperationFunc()函數進行添加。

5.2 繼承新簡單工廠類,重寫Init()函數。


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