其中讓我疑惑的地方在於「為什麼要說對『介面』,而不是父類別」,在似懂非懂的狀況下,我自行對這個概念作修改,成為「對介面或父類別撰碼」。
雖然現在證明這個修改是不佳的,不過得和大家分享的是,這種思考「概念是否正確,為什麼要這麼說」的想法,幫助我在程式領域的學習成長很大。
實際上我的修改,如果是在 Java 程式語言,也許沒有太大的問題。由於教導物件導向與設計原則的書大多是以 java 作為示範的對象,而 Java 這個對於物件思考有著大幅規劃的語言,很顯然在規劃的時候就試著去避免一些物件導向可能的問題。(註)
所以一直到我要面對我工作使用的 Flex ,而非書上範例的 Java 時,我才了解為什麼只能對「介面」而不是「父類別」寫程式。
Flex 由 Flash 的 ActionScript 的基礎發展而來,雖然 ActionScript 3.0 號稱是一個物件導向的程式語言,不過就我個人觀點來看,它還不夠完整。主要的原因是在於沒有完整的封裝規劃。舉一個例子,我不能宣告建構式(constructor)為私用(private),為了保護這個類別不被直接建立,而必須要呼叫其它靜態方法才能建立。所以,當我想要使用獨體模式(Singleton)設計時,只能依靠口頭或是文件的規範,卻無法由程式架構面去禁止別人「new」出這個類別。
從滿早之前發現一個奇怪的現象,在 flex 的類別中,有 IEventDispatcher 這個介面和 EventDispatcher 類別。感覺對於程式設計而言,是多此一舉的,因為 EventDispatcher 本身就可以作為所有能發出事件類別的父類別,以我由 Java 帶過來的觀念中,IEventDispatcher 這個介面顯得十分多餘。甚至在某些套件的範例程式出現底下這個奇怪現象…(取自網路上關於 PureMVC 的教學)
public class [類別名稱] extends Mediator implements IMediator
讓人不解的是,都已經繼承了「Mediator」類別了,為什麼還要去實作「IMediator」介面,兩者只多了一個「I」,這樣規劃有什麼樣的目的?
一直到我自己在規劃類別的時候,才知道在 Flex 的程式開發中,為什麼要這麼作…
因為 Flex 的物件導向有幾個限制,或著是說和 Java 比較起來少了一些東西,在 Flex 裡,是沒有抽象類別、動態函數這種。換一種說法就是「要嘛就把程式碼寫完(一般類別)要嘛就一行都不要寫(介面)」,基於這個原因,沒有辦法寫一個「作到一半」的類別,並要求繼承的子類別把其它沒實作完成的部分完成。這個特性造成一些問題,使得不能單純使用父類別定義滿足物件導向開發…
- 因為沒有動態類別,所以我使用類別的方式定義,無法藉編譯器之手,要求所有子類別完成應該被覆寫的方法。許多時候你會希望沒有覆寫的錯誤是發生在編譯期,而不是你的客戶拿到程式開始使用的時候。這樣修改的人就是編寫程式的人,而不是另一個倒霉的維護人員。
- 如果改用介面定義,則所有子類別共通的函數,就不無法進行集中,統一修改。許多子類別只是部分方法不同,大部分的方法,甚至是變數都是一致的。在 Flex 的介面只能定義公開方法,你不能要求所有子類別一定得去作某個公用的保護(同套件 or 繼承)函數。
為了解決這個問題,介面和父類別的使用被合在一起使用了。父類別只負責提供一些要共用的、能被繼承的方法和屬性,甚至將其中一部分方法作封裝或是 final 的處理,保護所有子類別的行為在這部分是一致的。而介面則定義要公開給其它類別使用的方法,當然啦,利用定義 getter 和 setter 方法,等同於定義公用屬性。所以「父類別和介面之間可能沒有直接的關係」,或換句話說為「父類別並沒有實作介面」。以上面的例子來說,「Mediator 可能並沒有實作 IMediator介面」只是具有許多介面裡定義的方法而已。
因此,就會出現同時繼承類別又實作方法的現象,光靠父類別和介面其中一種是無法滿足需求的。利用兩者的結合,才能達到接近 Java 動態類別、多型的效果。
至於為什麼要對「介面」而非對父類別撰碼,則是因為介面只是單純的定義有哪些方法,並不實作方法的行為。也就是說,當今天發現父類別的方法可能不適用於某些子類別,要另外抽出一組程式實作的時候會有比較高的彈性。這是基於「已經有許多依照此父類別撰寫的程式碼,程式不易改動的狀況下。」
用圖片來說明︰(標淺黄色的是要修改的類別)
如果是使用父類別的話…
為了抽出不同的方法,所以得修改「父類別」,並且把抽出的程式放到「新父類別1」中,才能讓原來的兩個子類別共用。但是因為要改變子類別的繼承指向到「新父類別1」,所以也得進行程式修改。
「新父類別2」和兩個「新子類別」都是需求要加入類別,所以不多作解釋。
如果改成使用介面,就會變成…
可以看出,只有要新增加的程式,原有的程式碼幾乎可以不作修改。可以減少許多錯誤的可能。
如果有人覺得兩個父類別有共用的方法,那還是變得很複雜吧!其實不會,如下圖…
重點在於大量原有的子類別不需要更動,也能確保它們依然可以正常運作。
所以對於介面撰碼,在物件導向上是比較有彈性的作法。當然啦~ 會因為這個原因,多出許多介面。
註︰
時至今日,我認為不應該稱 Java 為「極好」物件導向程式語言,它仍有一些限制,在執行程式的時候,記憶體的大量使用,仍是目前讓許程式人員頭痛的事情。
沒有留言:
張貼留言