SOLID 介面隔離原則
SOLID 介面隔離原則(ISP, Interface Segregation Principle Substitution Principle)
-5 min read「介面隔離原則」(Interface Segregation Principle,簡稱 ISP)是 SOLID 設計原則的其中一項,指出一個類別不應該強迫實作它用不到的方法,也就是介面應該要小而專精,而不是大而全面。這樣可以讓系統更加靈活,並且可以讓介面更容易被維護。
生活中的範例
電腦主機
桌電的主機的元件可以分為:主機板、記憶體、顯示卡、風扇、CPU ...等等,因為良好的介面隔離,每一個元件都是不同的介面,所以當使用者想要更換顯示卡,就只需要去買顯示卡就好,不需要買一個記憶體與顯示卡的結合體,或是維修的時候,只需要帶元件過去,不需要把整台電腦都帶過去。
垃圾分類
假設你的家裡有一個大垃圾桶,平常家裡的垃圾都會直接扔進去,不管是廚餘還是回收物,很難區分垃圾的種類,這樣可能會對環境造成傷害,在拿到社區回收時,往往需要重新分類,因為一開始沒有把分類(介面)設定好,所以造成了難以維護的情況。 為了解決這個問題,你可能會把垃圾桶分成不同的類別,例如廚餘桶、回收桶、可燃垃圾桶等等,且每個垃圾桶都需要套好垃圾袋。這樣每種垃圾都可以分類投放,不但對環境更加友善,也方便了垃圾處理工作。
在這個例子中,我們可以將垃圾桶視為一個介面,用來管理不同種類的垃圾,而每個垃圾桶就是實現這個介面的具體類別。這樣做的好處是,當你需要新增一個垃圾桶時,只需要實現這個介面,而不需要修改原有的程式碼,這樣也可以保證系統的穩定性。
相簿分類
假設你有一個相簿,裡面有很多照片,但是它們沒有分類,只是亂糟糟地放在一起。這樣的相簿很難找到你需要的照片,因為你需要一直從眾多照片中尋找。這裡的問題在於介面沒有被隔離,所有的照片都放在同一個地方,完全沒有分類,在查找並使用照片的時候,就會無法很靈活的使用。
但如果你把照片分成幾個相冊,例如旅遊、家庭、寵物等,每個相簿都只包含特定類型的照片,這樣你就可以更輕鬆地找到你需要的照片了。在這裡,你已經使用介面隔離原則來優化你的相簿,將不同類型的照片分類存放,讓你更容易找到需要的照片,也讓相簿的使用更加方便。
JavaScript 範例
錯誤範例
在這個範例中,在 Animal 中時做了 walk, swim, fly 方法,但並不是所有動物都會走路、游泳、飛行,反而還要在子類中複寫掉這些方法,因此並不符合介面隔離原則,一個類別不應該強迫實作它用不到的方法。
class Animal { walk() { console.log('我會走路') } swim() { console.log('我會游泳') } fly() { console.log('我會飛') }}class Duck extends Animal { quack() { console.log('我會嘎嘎叫') }}class Penguin extends Animal { fly() { console.log('我不能飛') // 錯誤的實現,企鵝不能飛 }}class Eagle extends Animal { swim() { console.log('我不能游泳') // 錯誤的實現,老鷹不能游泳 }}const duck = new Duck()duck.walk() // 我會走路duck.swim() // 我會游泳duck.quack() // 我會嘎嘎叫const penguin = new Penguin()penguin.walk() // 我會走路penguin.swim() // 我會游泳penguin.fly() // 我不能飛const eagle = new Eagle()eagle.walk() // 我會走路eagle.fly() // 我會飛eagle.swim() // 我不能游泳
正確範例
使用「CanSwim」和「CanFly」介面,分別定義了游泳和飛行的行為,並最小化分離介面。而鴨子「Duck」繼承了「CanSwim」介面,企鵝「Penguin」也繼承了「CanSwim」介面,而老鷹「Eagle」則繼承了「CanFly」介面,這樣子類就不會需要實作它用不到的方法,符合「介面隔離原則」。
class CanSwim { swim() { console.log('我會游泳') }}class CanFly { fly() { console.log('我會飛') }}class Duck extends CanSwim { quack() { console.log('我會嘎嘎叫') }}class Penguin extends CanSwim {}class Eagle extends CanFly {}const duck = new Duck()duck.swim() // 我會游泳duck.quack() // 我會嘎嘎叫const penguin = new Penguin()penguin.swim() // 我會游泳const eagle = new Eagle()eagle.fly() // 我會飛