SOLID 里氏替換原則
SOLID 里氏替換原則(LSP, Liskov Substitution Principle)
-4 min read這個原則是指,任何一個可以使用父類別(基礎類別)物件的地方,都應該能夠使用子類別(衍生類別)物件,而不會產生錯誤或不正確的結果。簡單來說,就是子類別要能夠替換掉父類別,而不會造成程式錯誤或行為異常。
- 先驗條件不可以強化:父類別要求的是矩形,子類別就不能要求得更嚴,只准人家給正方形
- 後驗條件不可以弱化:父類別產出的是正方形,子類別不能說沒關係啦,就給人家隨便一個矩形
- 不變條件必須保持不變:父類別是一個產生矩形的方法,子類別不能背骨,跑去產生圓形
子層的實作都應該符合父層的介面設計
生活中的範例
不能認為只要是鳥類都會飛行,這會同是鳥類企鵝尷尬
假設你認為鳥類都有翅膀,且都會飛行,那麼應該各種鳥類應該都會飛行,例如:雞、鴿子、企鵝、鴕鳥, 但是鴕鳥跟企鵝不會飛呀,所以這個設計是不符合里氏替換原則的。
應該將鳥類分為都有翅膀,並且在進一步分類為「會飛的鳥」與「不會飛的鳥」,
JavaScript 範例
錯誤範例
企鵝強制實現了 fly() 方法,但是不符合其特性:
class Bird { fly() { console.log('我正在飛行') }}class Pigeon extends Bird { // Pigeon 也可以飛行}class Penguin extends Bird { fly() { console.log('我正在飛行') // 錯誤的實現,企鵝不能飛行 }}const bird = new Pigeon()bird.fly() // 正常情況下可以飛行const penguin = new Penguin()penguin.fly() // 錯誤的行為,企鵝不應該飛行
正確範例
在這個修改後的程式碼中,Bird 類別有了一個新的特性 hasWings
,這個特性表明該類別的所有子類別都有翅膀。另外我們也新增了 FlyingBird 與 NonFlyingBird 這兩個子類別,分別表明該鳥類是否可以飛行。Pigeon 繼承 FlyingBird,因此它可以飛行;而 Penguin 繼承 NonFlyingBird,因此它不能飛行。
也因此符合 Liskov 替換原則,Pigeon 與 Penguin 都可以代替父類別 Bird,因為他們都有翅膀。
class Bird { constructor() { this.hasWings = true }}class FlyingBird extends Bird { constructor() { super() this.canFly = true } fly() { console.log('我正在飛行') }}class NonFlyingBird extends Bird { constructor() { super() this.canFly = false } fly() { console.log('我不能飛行') }}class Pigeon extends FlyingBird { constructor() { super() }}class Penguin extends NonFlyingBird { constructor() { super() }}const bird = new Pigeon()console.log(bird.hasWings) // trueconsole.log(bird.canFly) // truebird.fly() // 我正在飛行const penguin = new Penguin()console.log(penguin.hasWings) // trueconsole.log(penguin.canFly) // falsepenguin.fly() // 我不能飛行
參考文章
Table of Contents