在程序式語言中,如要打造可攜性介面,可透過類似建立物件以及多型函式指標的方式,將那些與平臺相關的資料變數及函式,通通集結在一塊,程式碼將因此變得更有組織、更容易理解。
運用物件導向觀念撰寫可攜性程式
在物件導向的觀念底下,我們可以將與平臺相關的動作及狀態,封裝成為個別的獨立物件,所有的物件都符合我們所制定的抽象化介面。在這介面之上,與平臺無關;介面之下,與平臺相關。
當我們使用正宗的物件導向程式語言時,需要的機制,語言本身都已提供,關鍵便在於如何運用。
使用物件導向程式語言時,如果想撰寫具可攜性的程式時,大抵上做法也是相同:
(1)設計抽象介面,以分離出與平臺相依的特性
(2)針對每個平臺,提供不同但都滿足此抽象介面的實作。
前一回中,我們看到了SDL中是如何以C實作SDL_VideoDevice這個會隨著平臺而異的「視訊設備」的抽象介面:
struct SDL_VideoDevice
{
const char *name;
int (*VideoInit) (_THIS);
…
int (*CreateWindow) (_THIS, SDL_Window * window);
int (*CreateWindow) (_THIS, SDL_Window * window);
int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);
void (*SetWindowTitle) (_THIS, SDL_Window * window);
void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);
void (*SetWindowPosition) (_THIS, SDL_Window * window);
void (*SetWindowSize) (_THIS, SDL_Window * window);
…
};
倘若以C++實作,便有可能會像是這樣:
class VideoDevice
{
private:
const char *name;
public:
virtual int VideoInit(void) = 0;
…
virtual int CreateWindow (SDL_Window * window) = 0;
virtual int CreateWindow(SDL_Window * window) = 0;
virtual int CreateWindowFrom (SDL_Window * window, const void *data) = 0;
virtual void SetWindowTitle (SDL_Window * window) = 0;
virtual void SetWindowIcon(SDL_Window * window, SDL_Surface * icon) = 0;
virtual void SetWindowPosition(SDL_Window * window) = 0;
virtual void SetWindowSize(SDL_Window * window) = 0;
…
};
VideoDevice本身是個類別,但其中的函式都被宣告為純粹虛擬(Pure Virtual)的函式。這意謂著,它其實只是做為一個抽象化的介面,無法產生出任何樣例(Instance)。因此,接著必須再為不同的平臺,分別實作這個類別裡的每一個虛擬函式。例如:
class Win32_VideoDevice : publicVideoDevice
{
…
};
class MacOSX_VideoDevice : publicVideoDevice
{
…
};
相較於模擬物件導向的方式,有一些動作就可以簡化或省略,例如不再需要模擬_THIS指標,同時毋需自行設定每個函式指標確切指到的函式。
Win32_VideoDevice及MacOSX_VideoDevice都繼承自VideoDevice,並且實作了所有的虛擬函式。透過這種方式,我們可以讓用戶端的程式碼,只碰觸到VideoDevice這個基礎類別(在產生出具體的衍生類別實作後,將其向上轉型至基礎類別),因此,對用戶端的程式碼而言,完全不會牽扯到任何平臺相關的程式碼。這些細目都隱藏起來了。
這正是多型的妙用所在。
站在物件的層次,分析出系統與平臺相異的部分
物件導向的思考方式,不僅幫助我們將與平臺相關的操作及資料變數群集在一塊,同時幫助我們站在物件的層次上,思考如何分析出系統中與平臺相異的部分。從命名便不難稍見端倪,當你將類別命名為VideoDevice時,便暗示著從物件的角度出發,用「視訊設備」這個抽象的物件概念,來指涉那些會隨著平臺而異的諸般操作,像是建立視窗、設定視窗標題、設定視窗大小等。
這麼一來,像是「音訊設備」、「搖桿」等控制方式隨平臺而異的東西,都可以利用物件導向的設計觀念,將它們視為物件,並且設計出每種物件的抽象介面。此後,基於這抽象介面而寫下的程式碼,自然就能確保其可攜性。
運用設計模式解決平臺對應的問題
除了我們反覆提到的介面化的技巧之外,最後我要提到一件事,當我們針對每個平臺都提供實作之後,在特定平臺執行時,究竟要如何對應到實作。
當我們寫C語言,並且使用一組鬆散的同名函式做為介面以及實作時,同時間只有一個平臺的程式碼會被編譯進去,因此不論是在那一個平臺上,都只會有一份實作,因此沒有對應的問題。
可是,當我們利用struct封裝每個平臺上各自相異的資料變數及程式碼時,便需要將struct中的每一個函式指標,指向特定平臺的函式實作。問題來了,該於何處指向?同時,這個strut的變數,又要於何處建立?
利用物件導向語言時,同樣無法逃脫同樣的問題。該如何建立所執行平臺上的實作物件?我們希望讓用戶端的程式碼只碰觸到基礎類別,也就是在產生出具體的衍生類別實作後,將其向上轉型至基礎類別,以便讓用戶端的程式碼完全不會牽扯到任何和平臺有關的程式碼。
針對這樣的問題,在設計模式裡有個好的解決方案——Factory。
Factory模式可協助撰寫跨平臺的可攜程式碼
之前介紹過 Factory這種用來負責產生物件實體的設計模式。我們可以再來回顧其中最單純的「Simple Factory Method」。
所謂的「Simple Factory Method」,就是指透過某個專門類別的特定Method,產生符合某一既定產品介面的實際產品。
對於想要保持平臺中立的用戶端程式碼而言,利用這個設計模式只需透過BaseClass(例如VideoDevice)產生真正的實作(例如Win32_VideoDevice或MacOSX_VideoDevice),它本身並不需要知道具體會是那一份實作,對它而言,一律都是BaseClass。唯一需要知道究竟是那一份實作的,就只有BaseClass,於是便可以將這個與平臺相關的資訊封裝於此處。
最重要的工作,仍是可攜性介面的設計
如果你稍微鑽研一下SDL中的程式碼,便會發現,即使它採用C語言寫成,但是,同樣運用了類似Factory生成模式的技巧,產生對應平臺的實作。
利用Factory可以讓用戶端程式碼更乾淨、更對平臺一無所悉。不過對於撰寫可攜性程式碼而言,它只是扮演一個加強的技巧。真正重要的工作,還是回歸到可攜性介面的設計,因為不論是採用程序式語言或物件導向語言的那一種實作方式,這介面設計的好壞,終究才是影響最為深遠的因素。
|
作者簡介─王建興 |
|
![]() |
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。 |
熱門新聞
2026-01-12
2026-01-12
2026-01-12
2026-01-12
2026-01-12
