(一)
有個class來表示網頁浏覽器:
class WebBrowser { public: void clearChache(); void clearHistory(); void removeCookies(); };
許多用戶會想一整個執行所有這些動作,因此WebBrowser也提供這樣一個函數:clearEverything
class WebBrowser { public: void clearChache(); void clearHistory(); void removeCookies(); void clearEverything(); };
當然也可以由一個non-member函數調用適當的member函數而提供出來:
void clearBrowser(WebBrowser wb) { wb.clearChache(); wb.clearHistory(); wb.removeCookies(); }
那麼哪一個比較好呢?是member函數clearEverything()還是non-member函數claseBrower呢。?
越多東西被封裝,我們改變那些東西的能力也就越大。對象內的數據。越少代碼看到它(訪問它),越多的數據可被封裝,也就越能自由地改變對象數據,例如改變成員變量的數量,類型等等。
所以答案當然是non-member函數claseBrower!因為它導致了較大的封裝性,因為它並不增加“能過訪問class內之private成分”的函數數量。這就解釋了為什麼clearBrowser(一個non_member non-friend函數)比clearEverything(一個member函數)更受歡迎的原因:它導致WebBrowser class有較大的封裝性。
但是這裡有兩件事情要注意下:第一:這裡只適用於non-member non-friend函數。friend函數對class private成員的訪問權力和member函數相同,因此兩者對封裝的沖擊也相同。
第二:只因在意封裝而讓函數“成為class 的non-member”,並不意味著它“不可以是另一個class的member”。假如我們可以另clearBrowser成為某工具類(utility class)的一個static member函數。
(二)
在C++中,比較自然的做法是讓clearBrowser成為一個non-member函數並且位於WebBrowser所在的同一個namespace(命名空間)內:
namespace WebBrowserStuff{ class WebBrowser{...}; void clearBrowser(WebBrowser wb); }
像clearBrowser這樣的函數是個“提供便利的函數”。一個像WebBrowser這樣的class可能擁有大量的便利函數,某些與書簽有關,某些與打印有關,某些與cookie的管理有關…,通常,大多數客戶只對其中某些感興趣。沒有道理一個只對書簽感興趣的客戶卻與一個cookie相關便利函數發生編譯相依關系。分離他們最直接的方法是將他們放入不同的頭文件:
//頭文件webbrowser.h這一頭文件針對class WebBrowser自身以及WebBrowser核心機能。
namespace WebBrowserStuff{ void clearBrowser(WebBrowser wb); ...//WebBrowser核心機能,幾乎所有客戶都需要的non-member函數。 } //頭文件“webbrowserbookmarks.h” namespace WebBrowserStuff{ class WebBrowser{...}; ...//與書簽相關的便利函數 } //頭文件“webbrowsercookies.h” namespace WebBrowserStuff{ class WebBrowser{...}; ...//與cookie相關的便利函數 }
C++標准程序庫正是這樣的組織方式。數十個頭文件,每個頭文件聲明std的某些機能。如果只想使用vector不用#include .這允許客戶只對他們所用的那一小部分系統形成編譯相依。這種切割機能並不適合class成員函數,因為class 必須整體定義,不能被分割為片段。namespace可以跨越多個源碼文件,而class不能。
將所有便利函數放在多個頭文件中但隸屬同一個命名空間,意味著客戶可以輕松擴展這一組便利函數。他們所要做的就是添加更多non-member non-friend函數到此命名空間內。例如:如果客戶想寫些與影像下載相關的便利函數,只要在WebBrowserStuff命名空間內建立一個頭文件,內含那些函數的聲明即可。新函數就像其他舊函數一樣可用且整合為一體。這是class無法提供的另一個性質,因為class定義對客戶是不能擴展的。
盡量用non-member non-friend函數替換member函數。可以增加封裝性、包裹彈性(packaging flexibility)、和機能擴充性。