2012年3月2日 星期五

[Programming] 物件導向(3) - 三相之力


三相之力。(From 魯阿魯)
好吧,我承認我很宅。


三相之力是LOL裡三種不同類型的中等武器合成的高級武器。會用三相之力來當副標是其來有自的。物件導向程式設計的概念中,有三種強大的能力,算是當年程式設計中跨時代的發明,分別是封裝(Encapsulation)、繼承(inheritance)、多型(polymorphism)。


封裝
如果要以一句話概括封裝的話,應該可以這麼說:外部的程式不能直接去存取物件裡的成員,而是必須透過特定的public存取權限的成員來存取物件內容。如果沒有封裝的概念,物件的意義就變得不怎麼重要了,C語言的struct也做得到同樣的事。把原本散成一團的程式封裝成一個一個的物件,就是為了讓程式裡物件和物件之間的依賴性變小,也就是隅合度變低,讓程式碼不會像一團陽春麵一樣,抓一條麵就把整團揪結在一起的麵全抓出來。
這應該不難理解。試著想像一下,如果你的封裝特性做得確實,外部程式在存取物件的時候就能透過幾個特定的方法而已,而這個物件實際經過哪些動作,外部的程式根本不需要知道。這時候,如果有什麼地方要改,看是物件裡面的動作,或是外部程式要改,而不至於需要把整隻程式和所有動作全部改掉。
然而,封裝只是建構物件導向設計的第一步而已....


繼承
有的時候,或很多時候,你會發現你需要用到的物件和其它物件只差一點點東西而已,只要稍微將原有的物件加以改寫或加工就能滿足你的需求。這時候,你不需要從頭到尾重新建置一遍,只要建立一個新類別,然後繼承那個需要加工的物件,就可以開始加工新類別來滿足自己的需求,而且不用動到一分一毫原有的類別(要動到的話也是可以)。
假如今天B類別繼承了A類別,我們會稱A類別是B類別的父類別(superclass)、B類別是A類別的子類別(subclass),此時,正如前述所說,B類別會擁有A類別的一切特徵,然後再看你要怎麼去寫B類別。
上述那使用繼承的案例,比較明顯的動機是重新使用(reuse)。事實上,繼承更強大的威力不只是reuse而已。透過繼承抽象類別,就能夠把不同的類別整合起來,產生一個更有彈性的架構。比方說,我有人、狗、貓三種類別,我就能夠把這三種類別相似的地方抽離出來,另外做出一個「動物」的抽象類別,並且讓人、狗、貓這三個類別繼承動物,之後,人狗貓這三種類別,同時也有動物的類別特性,之後在呼叫「動物」的時候,是能夠代入這三種類別的。幾乎所有設計模式(design patterns)都會用到繼承,而且都會用到抽象類別或介面的繼承。當我們日後介紹到抽象或設計模式的時候再詳細描述吧!
要注意的是,C++可以允許子類別繼承多個父類別,而Java和C#不行,詳細的原因不太清楚,只知道這種多重繼承會讓程式的複雜度提高很多,就算允許也不建議使用。


多型
簡單來說,它允許一個類別的同一個函數,在不同物件(被實體化的類別)裡做的事情不一樣,但是必須透過繼承才能達到這個目的。所以,先有繼承的概念,才能實現多型。
舉例:既然要繼承,當然必須有一個基層父類別A,還有繼承出來的子類別B和C。此時B和C就能夠透過override把原本A類別裡的方法a',根據自己的需求做改寫。讓B和C各別的a'執行自己的動作。
後來在拜讀了搞笑談軟工裡所介紹的多型,才明白其原理:今天如果呼叫一個物件裡的函式,並不是由呼叫函式的那一端 (sender) 來決定呼叫到的行為,而是由接收端來解釋。當我們由物件D裡呼叫B和C的a',執行的行為並不是由D來決定,而是由B和C來決定,所以,我們會在物件D裡得到B和C各別執行完a'的結果。




透過這三種概念,物件導向的發展空間開始擴大,設計出有彈性、可擴充、容易維護的軟體架構。
只要你會用的話。


ref:
[1] 思考物件導向(1)物件導向與封裝
[2] 搞笑談軟工

沒有留言: