十多年前讀大學時,我對於OO(Object-Orientation,物件導向)興致正濃,看了不少OO的書,有外文書,也有中文書。其中,中文書為了幫助讀者理解,都會用現實生活中的物件做比擬,比方說:哺乳動物、交通工具,我記得我讀過的一個範例中提到:「斑馬」繼承自「馬」。

學習OO的重點:封裝、繼承、多型
在工研院當實習生時,老闆要我報告OO,於是我舉了書上的例子,當老闆聽到我宣稱「斑馬繼承自馬」時,他開玩笑地說:「那麼馬子(女朋友)應該也是繼承自馬。」我深受羞辱,感覺被IT中文書荼毒了。

OO的三大基礎是封裝、繼承、多型。用現實生活的物件做OO解說上的比擬,通常不會太恰當,因為只能解釋封裝和繼承,卻無法解釋多型。而多型卻是OO真正的重點,也是學習OO的門檻。沒有解釋多型,就等於小學而大遺。

對於OO,比較恰當的例子是「形狀」,一來容易理解,二來適合同時解說封裝、繼承、多型。

我認為OO的書不用看太多,只要看Bertrand Meyer的名著《Object-Oriented Software Construction》第二版就夠了,但這本書可不薄。

封裝、繼承、多型這三者是有次序性的,沒有封裝就不可能有繼承、沒有繼承就不可能有多型。只支援封裝的語言稱為Object-Based語言(例如傳統的Visual Basic);同時支援封裝、繼承、多型的語言才能稱為OO語言(例如.NET時代的Visual Basic)。

有沒有某個語言只支援封裝和繼承,卻不支援多型?不會有語言這麼無聊,基本上繼承往往只是一個中間過程,真正的目的是多型。支援繼承,卻不支援多型,這是沒有意義的。

封裝
封裝(Encapsulation)的目的,是將程式碼切割成許多模組(Module),使各模組之間的關連性降到最低,這麼一來比較不會產生「牽一髮而動全身」的狀況,降低模組間相互依賴的程度,也等於是降低複雜度,讓開發與維護更容易。

事實上,沒有人用「模組」一詞來稱呼封裝的結果,而是稱為「類別」,把模組一詞做更高階的包裝用途。因此我們應該將「類別」視為封裝的結果,把「模組」視為整個程式切割出來的許多片段。在OO的世界,一般來說,一個程式有多個模組,一個模組內包含多個類別。

模組的概念不是OO獨具的,許多非OO語言也具有模組,但是OO的語言幾乎都具備模組,例如Java的Package;D語言的模組;而.NET更是細分成組件(Assembly)和模組,其實.NET的組件與模組都具備一般模組的概念,但程度有別(組件包含模組)。

封裝是以資料為核心,將相關的資料放在一起,把會用到這些資料的函式也放進來,等於將資料和函式放在一起。儘管有的語言還有其它東西,例如Event、Property,但是從內部來看,這些都是函式的變形。

為了和非OO的世界做出區隔,OO也做了一些名詞上的改變,將Function(函式)改稱為Method(方法)、將Call(呼叫)改稱為Invoke(調用)。但是新舊詞彙基本上還是通用的。

能見度
封裝的目的既然是要「降低互相依賴的程度」,就牽涉到能見度的問題:這個「類別/方法/欄位」該不該暴露給別的模組、同一個模組的不同類別、自己的「次類別」、友伴類別(Friend Class)、內部類別(Inner Class)?這就是所謂的「能見度」(Visibility)。

我們當然希望盡可能降低能見度,才能「降低互相依賴的程度」。別人不需要知道的,就不要讓它知道,這就是所謂的「資訊隱藏」(Information Hiding)。

最該被隱藏的是資料。極致的封裝主義者,主張所有資料一定都不可以直接被外部(包括次類別)存取。

上面提到,封裝將相關的資料和使用到這些資料的方法包成類別。最理想的狀況是,讓資料能見度降到最低,外面完全看不見,留下的對外介面(Interface)只剩下Method。換句話說,每個物件的Interface是一些方法的集合,完全沒有資料。

設定能見度不是一件容易的事,往往需要深思熟慮。特別是對於設計「框架」(Framework)的人來說,能見度設定得太寬,造成資訊隱藏效果不佳,可能會帶來相當多負面效果(例如複雜度提高、程式容易出錯、非Thread-Safe……等);能見度設定得太緊,則造成效率變差、擴充程度變差(有些設計因而做不出來)。(未完待續)

蔡學鏞-專職作家
清華大學資訊工程碩士,曾任華碩集團軟體工程師、元智大學資訊系講師、美商歐萊禮出版社技術編輯、臺灣微軟特約專欄作家。

相關閱讀:
思考物件導向(2)繼承與其階段性任務
思考物件導向(3)從多型看物件導向的真面目

熱門新聞

Advertisement