分析有關封裝和信息隱藏的誤區
封裝(Encapsulation)和信息隱藏,這兩個詞,我想大家都不會陌生。但是,有很多開發人員并沒有深入的了解,甚至存在一些誤區。今天就專門來說一下。
一、封裝
1、什么是封裝?
從字面意思來看,封裝就是把一些相關的 東西打包成一坨(看到“坨”這個量詞,不要想歪了)。“封裝”最廣為人知的例子,就是在面向對象編程(以下簡稱OOP)里面,把數據和針對該數據的操作,統一到一個class里。
2、封裝有啥好處?
那封裝有啥好處捏?一個主要的好處,就是增加軟件代碼的內聚性。通過增加內聚性,進而提高可復用性和可維護性。
3、封裝的手段
很多人把封裝的概念局限于類,認為只有OO中的class才算是封裝。這實際上是片面滴!在很多不使用"類"的場合,一樣能采用封裝的手法。下面俺隨手舉幾個和OO無關的例子:
a、通過文件
比如C和C++支持對頭文件的包含(#include)。因此,可以把一些相關的常量定義、類型定義、函數聲明,統統封裝 到某個頭文件中。
b、通過namespace/package/module
C++的namespace、Java的package、Python的module,想必各自的開發人員都很熟悉。這些語法雖然稱呼各不相同,但具有相同的本質。因此,也可以利用這些語法,來進行封裝。
二、信息隱藏
1、什么是信息隱藏?
“信息隱藏”——顧名思義——就是把不該暴露的信息藏起來。說到信息隱藏,很多人自然而然地,就聯想到某些OO語言(比如C++、Java)提供的,諸如private、protected之類的關鍵字。這些關鍵字可以通過訪問控制,來達到信息隱藏的目的。
2、信息隱藏有啥好處?
信息隱藏的好處,正好和“封裝”的好處相呼應。封裝是為了提高內聚性;而信息隱藏是為了降低耦合性。通過降低耦合,一樣可以達到提高可復用性、可維護性這2個目的。
3、信息隱藏的手段
和封裝類似,很多程序員也把信息隱藏的概念片面化——認為信息隱藏僅限于private和protected關鍵字。所以,俺再隨手舉幾個其它的信息隱藏手段。
a、通過接口類
可以通過定義接口類(Java中的interface、C++中的純虛類)來實現信息隱藏。具體實現如下:
定義一個接口類,僅包含一些公有的成員函數的聲明 (Java的abstract函數,C++的純虛函數),沒有任何函數實現,也沒有任何成員變量。然后把具體的實現代碼放到該接口類的一個派生子類中。
由于調用者只看到接口類,看不到實現類。所以,同樣可以達到信息隱藏。在某些情況下,使用這種手段達到的效果,會比基于訪問控制(使用private關鍵字)的效果,更好。
不過這種手段依賴于語言的支持。使用該手法的編程語言,至少要支持繼承、虛函數等語法。
b、通過pimpl手法
pimpl手法也叫作“Opaque Pointer”手法。和接口類的手法不同,pimpl手法不需要靠繼承、虛函數等語法的支持,因此對諸如C語言來說,很有用。
三、一些理解上的誤區
介紹完一些基本概念,再來說一下,關于封裝、信息隱藏的一些常見誤區。
1、把封裝等同于信息隱藏
這是混淆最嚴重的一個誤區——很多初學OOP的同學都把封裝和信息隱藏混為一談。希望經過俺前面的一番解釋,有些人能搞明白其中的差異。順便提一下,有個老外寫了篇小有名氣的文章——“Encapsulation is not information hiding ”。洋文好的同學,可以去瞅一瞅。
2、把封裝看得太狹隘
其實前面已經通過舉例,駁斥了狹隘看待封裝的誤區。此處不再啰嗦。
3、把信息隱藏看得太狹隘
前面已經通過舉例,駁斥了狹隘看待信息隱藏的誤區。此處不再啰嗦。
4、混淆可訪問性和可見性
考慮到某些網友可能連這兩者的區別都不太清楚,先簡單解釋一下。所謂可訪問性,就是可以對某個東西進行讀/寫操作;所謂可見性,就是能夠感覺到某個東西的存在。
前面談到信息隱藏時,我們提及了通過訪問控制的關鍵字(private、protected)來達到信息隱藏的目的。有很多同學認為這幾個關鍵字不光禁止了可訪問性(accessibility),還禁止了可見性(visibility)。其實也不盡然。不同的編程語言,對這兩者的處理是不同滴。比如在 C++語言中,類的私有成員雖然不可訪問,但還是可見的。此話怎講捏?請看下面的例子:
- int n = 0;
- class Parent
- {
- public:
- Parent()
- {
- n = 1;
- }
- private:
- int n;
- };
- class Child : public Parent
- {
- public:
- Child()
- {
- }
- void Func()
- {
- ::printf("%d", n);
- }
- };
- int main()
- {
- Child c;
- c.Func();
- return 0;
- }
如果俺問列位看官,程序執行后會打印出啥?相信一多半的同學會回答:“打印0”。
但是,真相是:該程序根本在編譯時就報錯了。
問題在于,父類的私有成員變量n雖然對子類是無法訪問的,但依然是可見的(可感知的)。所以,對于那個printf語句,編譯器會認為是企圖訪問父類的私有成員,故而報錯。
再悄悄跟大伙兒說一下,這個例子是俺從C++它爹所寫的《The Design and Evolution of C++》里面剽竊滴。
四、結尾
今天的話題,基本上到此結束了。***,俺想提醒列位看官注意一下:象封裝和信息隱藏,都屬于編程的基本功,為啥很多開發人員都沒有想透徹捏?
如果大家有什么不明白的,可以參見原文,下面是原文地址。
本文出處:http://blog.csdn.net/program_think/archive/2010/08/29/5846881.aspx
【編輯推薦】