Polymorphism中文一般稱為「多型」,早期也有人翻譯成「同名異式」。我比較喜歡「多型」,「多型」讓人覺得物件能夠以「多種面貌」出現,同名異式則太強調「不同的函式」。其實,「型別的不同」是「因」,而「函式的不同」是「果」。當一個物件具有不同的型別,就有可能會引發多型機制。

一個物件為何會有不同型別?這是因為繼承而來,物件可以扮演所有祖先類別的角色。例如當某物件的類別是Sub,當此物件被「轉型」成超類別Super之後,此物件就具有兩種不同的類別:「實際類別」是Sub,「形式類別」是Super。如果,在這個情況下呼叫此物件的方法m,會執行到Super定義的方法m?還是Sub定義(修改)的方法m?

答案是實際類別的方法,也就是Sub定義的方法m。因此,所謂的多型就是:不管形式類別是什麼,一定會執行到實際類別的方法。

你可能會覺得疑惑,為何當初要將物件轉型為祖先類別,導致「形式類別」(宣告類別)和「實際類別」(定義類別)不一樣?這個問題留待下一回再回答。

如果你的程式大量使用switch/case語法,很有可能是你的設計不良,沒有好好地使用多型。建議你最好「重構」(Refactoring)程式。

類別的方法,可以分成虛擬(Virtual)與非虛擬兩種。只有虛擬方法才能搭配多型機制使用。如果是非虛擬方法,則會執行到形式類別(而非實際類別)的方法,因為多型沒有發揮作用。

應用框架
為了方便軟體的開發,許多軟體廠商都會提供應用框架(Application Framework),現今流行的框架相當多,例如:.NET Framework、Borland VCL、Java Class Library。有了框架,我們終於可以享受到OO的好處,重複利用別人寫好的程式碼,不用一切自己重頭寫。

框架廠商先寫好大部分的程式,編程員只需要「利用繼承來做修改」,就能套用整個框架,為了讓你修改的部分能夠確實被執行到(而不是執行到框架本身的方法),所以這些允許修改的方法都是定義成虛擬的。

因為編程員「利用繼承來做修改」,所以產生次類別和重新定義的方法。框架比這個次類別更早被定義,當然不認識這個次類別,所以框架內都是以此次類別的祖先類別為「形式上」的處理對象(處理介面)。當此次類別物件被傳入框架中,就會被自動轉型成祖先類別,因此產生「形式類別」和「實際類別」的差異。正因為這樣的類別差異,加上次類別有重新定義方法,所以多型機制出現了。

單一繼承架構中,良好的框架設計(例如Java Swing)會將程式碼不需要被修改的部分,設計成類別。至於需要被繼承修改的部分,設計成介面(介面的方法全都是虛擬的),以及實踐這些介面的類別。框架內的類別盡量只使用到這些介面。

框架設計近年來比較不同的是,階層有變深的趨勢(例如AIR和WPF的框架);也就是說,繼承樹的葉節點到根節點之間的距離變大。這樣的好處是程式碼重複利用度增加(所以框架檔案體積變小),以及介面重複利用度增加(學習速度可以加快)。

OO是生產力的最終解答?
與物件導向程式設計關係緊密的是前一個階段「設計」,和下一個階段「測試」。「設計模式」(Design Pattern)將許多好的設計整理出來,讓我們設計功力大增。如果既有的設計不太好,你可以利用「重構」(Refactoring)的技巧來重新整理程式。現在講求TDD(Test-Driven Development,測試驅動開發),對OO來說,「單元測試」(Unit Test)正是以類別為最小單元。

千萬別忘了UML!設計OO系統時,UML可以整理你的想法,方便大家溝通,甚至當MDA(Model Driven Architecture,模式驅動結構)成熟之後,號稱可以用UML把架構設計圖畫出來,用OCL(Object Constraint Language,物件化語言)描述規範,然後就可以產生程式碼。

OO太美好了!OO是軟體開發的極致靈丹!……。繼續作夢吧!儘管OO主宰現今的主流語言、對開發效率似乎有一些提升,但我可沒看過什麼人用了OO之後就若有神助。更不用說OO還有學習門檻、各種OO框架/設計模式的學習曲線、過度工程化的問題。

最近,我覺得真正可以達到更高生產力的關鍵在於更高階的抽象,也就是DSL(Domain Specific Language,特定領域語言)。關於DSL,我將另闢專文介紹。

至於OO,在我有了新歡DSL之後,早就被我打入冷宮了。

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

相關閱讀:
思考物件導向(1)物件導向與封裝
思考物件導向(2)繼承與其階段性任務

熱門新聞

Advertisement