想象一個象征 Web 瀏覽器的類。在大量的函數(shù)中,這樣一個類也許會提供清空已下載成分的緩存。清空已訪問 URLs 的歷史,以及從系統(tǒng)移除所有 cookies 的功能:
class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
很多用戶希望能一起執(zhí)行全部這些動作,所以 WebBrowser 可能也會提供一個函數(shù)去這樣做:
class WebBrowser {
public:
...
void clearEverything(); // calls clearCache, clearHistory,
// and removeCookies
...
};
當(dāng)然,這個功能也能通過非成員函數(shù)調(diào)用適當(dāng)?shù)某蓡T函數(shù)來提供:
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
那么哪個更好呢,成員函數(shù) clearEverything 還是非成員函數(shù) clearBrowser?
面性對象原則指出:數(shù)據(jù)和對它們進(jìn)行操作的函數(shù)應(yīng)該被綁定到一起,而且建議成員函數(shù)是更好的選擇。不幸的是,這個建議是不正確的。它產(chǎn)生于對面向?qū)ο笫鞘裁吹囊粋€誤解。面向?qū)ο笤瓌t指出數(shù)據(jù)應(yīng)該盡可能被封裝。與直覺不同,成員函數(shù) clearEverything 居然會造成比非成員函數(shù) clearBrowser 更差的封裝性。此外,提供非成員函數(shù)允許 WebBrowser 相關(guān)功能的更大的包裝彈性,而且,可以獲得更少的編譯依賴和 WebBrowser 擴(kuò)展性的增進(jìn)。因而,在很多方面非成員方法比一個成員函數(shù)更好。理解它的原因是非常重要的。
我們將從封裝開始。如果某物被封裝,它被從視線中隱藏。越多的東西被封裝,就越少有東西能看見它。越少有東西能看見它,我們改變它的彈性就越大,因為我們的改變僅僅直接影響那些能看見我們變了什么的東西。某物的封裝性越強(qiáng),那么我們改變它的能力就越強(qiáng)。這就是將封裝的價值評價為第一的原因:它為我們提供一種改變事情的彈性,而僅僅影響有限的客戶。
結(jié)合一個對象考慮數(shù)據(jù)。越少有代碼能看到數(shù)據(jù)(也就是說,訪問它),數(shù)據(jù)封裝性就越強(qiáng),我們改變對象的數(shù)據(jù)的特性的自由也就越大,比如,數(shù)據(jù)成員的數(shù)量,它們的類型,等等。作為多少代碼能看到一塊數(shù)據(jù)的粗糙的尺度,我們可以計數(shù)能訪問那塊數(shù)據(jù)的函數(shù)的數(shù)量:越多函數(shù)能訪問它,數(shù)據(jù)的封裝性就越弱。
數(shù)據(jù)成員應(yīng)該是 private 的,因為如果它們不是,就有無限量的函數(shù)能訪問它們。它們根本就沒有封裝。對于 private 數(shù)據(jù)成員,能訪問他們的函數(shù)的數(shù)量就是類的成員函數(shù)的數(shù)量加上友元函數(shù)的數(shù)量,因為只有成員和友元能訪問 private 成員。假設(shè)在一個成員函數(shù)(能訪問的不只是一個類的 private 數(shù)據(jù),還有 private 函數(shù),枚舉,typedefs,等等)和一個提供同樣功能的非成員非友元函數(shù)(不能訪問上述那些東西)之間有一個選擇,能獲得更強(qiáng)封裝性的選擇是非成員非友元函數(shù),因為它不會增加能訪問類的 private 部分的函數(shù)的數(shù)量。這就解釋了為什么 clearBrowser(非成員非友元函數(shù))比 clearEverything(成員函數(shù))更可?。核転?WebBrowser 獲得更強(qiáng)的封裝性。
在這一點,有兩件事值得注意。首先,這個論證只適用于非成員非友元函數(shù)。友元能像成員函數(shù)一樣訪問一個類的 private 成員,因此同樣影響封裝。從封裝的觀點看,選擇不是在成員和非成員函數(shù)之間,而是在成員函數(shù)和非成員非友元函數(shù)之間。(當(dāng)然,封裝并不是僅有的觀點,如果觀點來自隱式類型轉(zhuǎn)換,選擇就是在成員和非成員函數(shù)之間。)
需要注意的第二件事是,如果僅僅是為了關(guān)注封裝,則可以指出,一個函數(shù)是一個類的非成員并不意味著它不可以是另一個類的成員。這對于習(xí)慣了所有函數(shù)必須屬于類的語言(例如,Eiffel,Java,C#,等等)的程序員是一個適度的安慰。例如,我們可以使 clearBrowser 成為一個 utility 類的 static 成員函數(shù)。只要它不是 WebBrowser 的一部分(或友元),它就不會影響 WebBrowser 的 private 成員的封裝。
class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
很多用戶希望能一起執(zhí)行全部這些動作,所以 WebBrowser 可能也會提供一個函數(shù)去這樣做:
class WebBrowser {
public:
...
void clearEverything(); // calls clearCache, clearHistory,
// and removeCookies
...
};
當(dāng)然,這個功能也能通過非成員函數(shù)調(diào)用適當(dāng)?shù)某蓡T函數(shù)來提供:
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
那么哪個更好呢,成員函數(shù) clearEverything 還是非成員函數(shù) clearBrowser?
面性對象原則指出:數(shù)據(jù)和對它們進(jìn)行操作的函數(shù)應(yīng)該被綁定到一起,而且建議成員函數(shù)是更好的選擇。不幸的是,這個建議是不正確的。它產(chǎn)生于對面向?qū)ο笫鞘裁吹囊粋€誤解。面向?qū)ο笤瓌t指出數(shù)據(jù)應(yīng)該盡可能被封裝。與直覺不同,成員函數(shù) clearEverything 居然會造成比非成員函數(shù) clearBrowser 更差的封裝性。此外,提供非成員函數(shù)允許 WebBrowser 相關(guān)功能的更大的包裝彈性,而且,可以獲得更少的編譯依賴和 WebBrowser 擴(kuò)展性的增進(jìn)。因而,在很多方面非成員方法比一個成員函數(shù)更好。理解它的原因是非常重要的。
我們將從封裝開始。如果某物被封裝,它被從視線中隱藏。越多的東西被封裝,就越少有東西能看見它。越少有東西能看見它,我們改變它的彈性就越大,因為我們的改變僅僅直接影響那些能看見我們變了什么的東西。某物的封裝性越強(qiáng),那么我們改變它的能力就越強(qiáng)。這就是將封裝的價值評價為第一的原因:它為我們提供一種改變事情的彈性,而僅僅影響有限的客戶。
結(jié)合一個對象考慮數(shù)據(jù)。越少有代碼能看到數(shù)據(jù)(也就是說,訪問它),數(shù)據(jù)封裝性就越強(qiáng),我們改變對象的數(shù)據(jù)的特性的自由也就越大,比如,數(shù)據(jù)成員的數(shù)量,它們的類型,等等。作為多少代碼能看到一塊數(shù)據(jù)的粗糙的尺度,我們可以計數(shù)能訪問那塊數(shù)據(jù)的函數(shù)的數(shù)量:越多函數(shù)能訪問它,數(shù)據(jù)的封裝性就越弱。
數(shù)據(jù)成員應(yīng)該是 private 的,因為如果它們不是,就有無限量的函數(shù)能訪問它們。它們根本就沒有封裝。對于 private 數(shù)據(jù)成員,能訪問他們的函數(shù)的數(shù)量就是類的成員函數(shù)的數(shù)量加上友元函數(shù)的數(shù)量,因為只有成員和友元能訪問 private 成員。假設(shè)在一個成員函數(shù)(能訪問的不只是一個類的 private 數(shù)據(jù),還有 private 函數(shù),枚舉,typedefs,等等)和一個提供同樣功能的非成員非友元函數(shù)(不能訪問上述那些東西)之間有一個選擇,能獲得更強(qiáng)封裝性的選擇是非成員非友元函數(shù),因為它不會增加能訪問類的 private 部分的函數(shù)的數(shù)量。這就解釋了為什么 clearBrowser(非成員非友元函數(shù))比 clearEverything(成員函數(shù))更可?。核転?WebBrowser 獲得更強(qiáng)的封裝性。
在這一點,有兩件事值得注意。首先,這個論證只適用于非成員非友元函數(shù)。友元能像成員函數(shù)一樣訪問一個類的 private 成員,因此同樣影響封裝。從封裝的觀點看,選擇不是在成員和非成員函數(shù)之間,而是在成員函數(shù)和非成員非友元函數(shù)之間。(當(dāng)然,封裝并不是僅有的觀點,如果觀點來自隱式類型轉(zhuǎn)換,選擇就是在成員和非成員函數(shù)之間。)
需要注意的第二件事是,如果僅僅是為了關(guān)注封裝,則可以指出,一個函數(shù)是一個類的非成員并不意味著它不可以是另一個類的成員。這對于習(xí)慣了所有函數(shù)必須屬于類的語言(例如,Eiffel,Java,C#,等等)的程序員是一個適度的安慰。例如,我們可以使 clearBrowser 成為一個 utility 類的 static 成員函數(shù)。只要它不是 WebBrowser 的一部分(或友元),它就不會影響 WebBrowser 的 private 成員的封裝。