抽象與介面

elliot
Feb 4, 2024

--

抽象是為了表達 is-a,並且優雅的解決多型,已達 減少重複程式碼。 例如我設計了一款 自動屠宰豬系統,只要是豬都能使用,在這種情境下,我想表達是輸入不管是哪種豬種 (台灣豬、伊比利豬、六堆黑豬等等),他們都有豬的特徵,都能夠套用。

介面則是用來表達 behaves like,比起抽象的減少重複程式碼,更側重的是解耦。 介面是一種 對於行為的一種 抽象,更像是一種協定、一種契約。 例如我不想出門買午餐,那我需要符合 “能幫我送達午餐” 這種行為的角色,那麼 pizza、麥當勞歡樂送、uber Eat、一個便當就外送的商家 都能滿足我。

Question: 我們一定需要介面嗎?

否,事實上我們可以建立一個抽象類別,其中沒有任何的屬性,並且將所有的方法都定義成抽象,就可以當成一種介面來使用。

甚至在有些動態語言裡,並沒有抽象,更多的是使用結構或類別來定義通用行為。此時我們可以建立一個一般類別,強迫其他類別在繼承該類別後必須去覆寫該方法,否則運行時拋出一個例外。

Question: 那..到底該如何決定選擇使用抽象還是介面?

最主要的判斷依據,就是依據你的需求想表達的是, is-a 的關係 還是 behaves like 的關係,如果主要是想減少重複的程式碼,請選用抽象。 如果是強調抽象的行為規範,則使用介面。

就實務開發上,會發現其實 抽象 更像是 我先有了ㄧ堆子類別的程式碼重複,進而將 重複項目 搬移到 父類別(抽象化),是一種 下而上 的設計思路。
而 介面 反而是一種 上而下 的設計思路,我們先設計介面,再去考慮具體的實現方式。

換句話說,繼承 和 組合實務上該如何抉擇呢?

繼承適合運用在簡單的場景,當層次過深 場景過於複雜時,反而不好維護
甚至有的時候為了程式碼共用,硬是生了一個基類別,但實質上並沒有繼承關係,既不是父子,也不是兄弟。

回頭看,繼承的主要功能是 用於 表達 is-a 關係, 程式碼複用,表示多型

但這三個功能是都可以被替代的,例如使用 behaves like , 使用組合加上 委託 解決程式碼複用,使用介面表示多型。 而且組合擅長於複雜的場景,雖然這也代表需要拆分得更細,成本較高,所以在於一般業務場景不複雜時可以使用繼承。

但我們無法預知未來,現在穩定的繼承關係將來未必穩定,且透過界面 + 組合 + 委託的方式 帶來的效益更大,故漸漸的大家開始鼓吹使用 組合大於繼承巴。

--

--