1. 程式人生 > >通過例子學設計模式之--介面卡模式以及使用場景說明(C++實現)

通過例子學設計模式之--介面卡模式以及使用場景說明(C++實現)

          介面卡模式的定義:介面卡模式將一個類的介面轉換成客戶期望的另一個介面,讓原本不相容的介面可以合作無間。
          該模式應該好理解。比如電源介面卡(中國和歐洲分別是電源220V,110V)就是該模式的一種表現。關於類圖或者其他說明我這邊就不囉嗦了。很多開發者包括我自己也是,更多困惑是不知道在實際的專案中該如何使用設計模式。本文希望通過例子來說明什麼情況下使用介面卡模式,算是拋磚引玉吧。
          使用場景:我們準備開發一個新模組或者功能。現在的情況是我們已經有現成可用的模組或者第三方庫,這些模組經過客戶測試是完全符合需求的,而且是穩定的。當然大家都希望直接呼叫這些模組,而不是費時費力的從頭開發。問題是通過分析現有模組的介面或者第三方庫的時候,發現直接呼叫比較麻煩或者不符合現在的程式設計模式。比如資料結構的定義不一致,比如返回值不一致,比如介面引數不一致(很多時候第三方庫考慮各種相容性,而我們其實不一定需要)。
          怎麼辦呢?好辦,增加一個"中間層"解決。這個中間層就是"介面卡模式"。如果不增加這個"中間層",或多或少會打亂我們現有的介面定義或者編碼方式。
          實現方式:
一般是通過類介面卡或者是通過物件介面卡。如何區分這2種呢?類介面卡是通過"多繼承",即繼承的方式實現,耦合性很高,父類變化了子類就需要重新編譯連結,而且"多繼承"容易產生二義性,不建議使用。物件介面卡是通過物件組合"的方式實現,耦合性低,通常使用該方式。符合面向物件設計的一個基本原則"優先使用組合,而不是繼承"。   
          OK,最後還是通過例子來說明吧:

    
/* 被適配者,Adaptee */
class API3rd
{
public:
	int fun1(int param1,int param2,float f1,float f2)
	{
		printf("API3rd::fun1\r\n");
		return 1;
	}
	float fun2()
	{
		printf("API3rd::fun2\r\n");
		return 0.1;
	}
};

/* Target類 */
class MyClass 
{
public:
	MyClass(){};
	virtual ~MyClass(){}
	virtual int f1(int param1)
	{
		printf("MyClass::f1\r\n");
		return 0;
	}
	virtual int f2()
	{
		printf("MyClass::f2\r\n");
		return 0;
	}
};

/* 類介面卡,使用多繼承 */
class Adapter1 : private API3rd,private MyClass
{
public:
	virtual int f1(int param1)
	{
		printf("Adapter1::f1\r\n");
		printf("由於API提供的介面引數太多,我們只需要關注一個引數即可。因此使用介面卡模式對該介面適配。\r\n");
		return API3rd::fun1(param1,0,0.0,0.0);
	}
	virtual int f2()
	{
		printf("Adapter1::f2\r\n");
		printf("由於API提供的介面的返回值不符合我們的需要,因此使用介面卡模式對該介面進行適配\r\n");
		float f = API3rd::fun2();
		return (f+0.5);
	}
};


/* 物件介面卡,使用物件組合的方式代替繼承 */
class Adapter2 : private MyClass
{
public:
	Adapter2()
	{
		m_api = new API3rd();
	}
	~Adapter2()
	{
		if(m_api)
		{
			delete m_api;
		}
	}
	
	virtual int f1(int param1)
	{
		printf("Adapter2::f1\r\n");
		printf("由於API提供的介面引數太多,我們只需要關注一個引數即可。因此使用介面卡模式對該介面適配。\r\n");
		return m_api->fun1(param1,0,0.0,0.0);
	}
	virtual int f2()
	{
		printf("Adapter2::f2\r\n");
		printf("由於API提供的介面的返回值不符合我們的需要,因此使用介面卡模式對該介面進行適配\r\n");
		float f = m_api->fun2();
		return (f+0.5);
	}
private:
	API3rd* m_api;
};


/* Client 呼叫場景 */
void TestAdapter()
{
	Adapter1* ap1 = new Adapter1;
	ap1->f1(2);
	ap1->f2();

	Adapter2* ap2 = new Adapter2;
	ap2->f1(2);
	ap2->f2();

	delete ap1;
	delete ap2;
}