2011-09-01 66 views
4

我一直在使用兩個庫,SFML和Box2D,同時也非常痛苦,以確保它們的函數或類都不會暴露在我的代碼的主體中,將它們隱藏在僅僅服務於行爲的類後面作爲我的代碼和庫本身之間的中介。我的調解員採取以下形式:外部庫應該包裝?

class MyWindow{ 
    public: 
     // could be 10 or so functions like below 
     int doSomething(int arg){ 
      return library_window->doSomething(arg); 
     }; 
    private: 
     library::window * library_window; 
    }; 

的好處這一點,至少是有人告訴我,是我的主要代碼身體是不是在圖書館依賴,以這樣一種方式,如果它的變化或我選擇使用另一個,比如說SDL或OpenGL來代替SFML或者其他的東西,我可以通過修改中介類來切換。但是,爲每個要使用的功能編碼接入點的痛苦是痛苦和重複的...

這真的是專業程序員應該如何處理外部庫?這值得嗎?

我是否正在做這個權利?

回答

3

你所描述的包裝技術的問題是你的包裝是透明的(真正意義上的這個詞) - 庫中的每個方法仍然可以通過包裝看到,具有相同的語義,相同的先決條件等等。你可以「看穿」你的包裝。

像這樣的透明包裝只有在某天將基礎庫切換到具有相同語義或至少幾乎完全相同的語義時纔對您有用。考慮這個例子。比方說,圖書館是在std :: fstream的,和你的應用程序讀取和寫入文件所需,並讓說你努力寫了一個包裝:

class MyFile { 
    std::fstream* fst; 
public: 
    void writeData(void* data, size_t count) { 
     fst->write((const char*) data, count); 
    } 
    void readData(void* buffer, size_t count) { 
     fst->read((char*) data, count); 
    } 
    // etc, etc. 
}; 

現在讓我們假設你希望(或需要)切換到具有非阻塞讀取和寫入的異步I/O。你的透明包裝將無法幫助你實現這一轉變。異步讀取需要兩種方法,一種是開始讀取操作,另一種是確認讀取已完成。它還要求應用程序承諾在這兩個方法調用之間不會使用緩衝區。

當所有的事情都說完之後,只有在非常仔細地設計不透明(好的接口是不透明的)時,庫接口包裝器纔有用。此外,爲了有用,你要包裝的圖書館必須是你熟悉的東西。因此,boost :: filesystem可以「包裝」DOS和Unix的路徑名,因爲作者熟悉POSIX,UNIX和DOS路徑名,並且正在設計「封裝器」來有效地封裝這些實現。

從你所描述的,在我看來,你的努力將最終浪費。簡單比複雜更好,除非封裝器真的封裝了某些東西(即隱藏底層庫),否則直接比間接更好。

這不是寫意大利麪的許可證 - 您的應用程序仍然需要主要組件的結構和隔離(例如,將UI與應用程序提供的實際計算/模擬/文檔隔離開來)。如果你這樣做了,有一天換一個庫是一個沒有任何包裝代碼的可管理的任務。

+0

透明與不透明類比是一種可視化的好方法,謝謝!標記這個答案,因爲它也觸及了其他答案的要點,並將它們作爲一個整體整合在一起。我將從頭開始重寫我的包裝,這次增加了功能和簡單性,而不是僅僅懶惰地放置在庫的頂部。 –

1

這取決於。

如果您使用的是非常成熟的庫,並且您可能不會遷移到其他實現,則中介類不是必需的。例如,你有沒有封裝stl或boost庫?另一方面,如果你使用的庫是新的,或者有很多替代品,那麼抽象可能是有用的。

+0

我認爲他們都非常成熟,所以它會落入第一位。 ......好吧,也許不像STL那樣成熟,但足夠成熟! –

4

不值得。只需使用這些庫。如果您最終希望更改爲不同的第三方庫,則最終需要更改應用程序代碼......否則,如果兩個版本中的所有內容都相同,那麼首先要做出什麼改變無論如何。

朋友不讓朋友過度工程。拒絕吧。

+0

我不能爭辯,哈哈 –

+1

在這種情況下,它並沒有真正傷害這樣做,因爲它只是更容易調用一個方法來完成您需要的工作,而不是直接將庫編碼到應用程序中,如果你知道你會添加更多,這將在稍後變得混亂。 –

1

是的,這是正確的,你應該編寫你的程序需要的基本功能,並編寫一個包裝(冗餘......)庫來做你所需要的。只需添加一個新的庫,您只需編寫一個新的包裝器,您就可以簡單地從程序下面替換底層包裝器,而無需擔心。如果你分開了後顧之憂,那麼稍後它更容易添加功能,因爲你不需要找到你正在使用函數的地方,或者使用複雜的#ifdef語句來切換庫,你可以使用一些簡單的#ifdef來定義類似

#ifdef LIB_A 
typedef MyGfxClassA MyGfxClass; 
#endif 

等等

+0

我很欣賞答案,它適用於一個體面的包裝。但是我發現我的「包裝器」的實現在更基礎的層面上存在缺陷,正如其他人指出的那樣,這是一種「1對1翻譯」。 –

+0

包裝應該是所需功能的翻譯。如果你需要繪製一個正方形,你就有一個drawSquare方法,你可以根據這個方法構建你的應用程序邏輯,但是隻有庫需要關心應用程序。 –

1

,如果你想提供一個簡單的接口(Convention over Configuration)這不是一個壞主意。但是,如果你只是要提供庫中所有實用程序的一對一翻譯,那麼它並不是真的值得。

+0

1比1的比例是我誤解了包裝的目的。實際上我有一些類可以簡化庫,使我最常用的「默認」和最簡單的使用。我只想補充一點,考慮那些幫助我的「包裝」的類和功能,並擺脫他們和圖書館之間不必要的委託。謝謝 –

2

你應該換東西在兩種情況下:

  1. 你有理由相信你會改變它。而我的意思不是說「好吧,有一天,也許挺好。」我的意思是你有一些真正的信念,你可能會改變圖書館。或者,如果您需要支持多個庫。也許你可以選擇使用SDL和SFML。管他呢。

  2. 您正在提供該功能的抽象。也就是說,你不只是做一個薄包裝,你在上改進了的功能。簡化界面,添加功能等。

+0

按照這兩條規定,我沒有號碼1,我沒有通過號碼2。對我來說選擇很清楚 –

+0

這是一個很難接聽的電話,幾乎讓你的答案簡潔。如果我能兩次upvote它,但我會。我喜歡我的問題是有太多好的答案。 –