SOLID 里氏替換原則
SOLID 里氏替換原則(LSP, Liskov Substitution Principle)
· 2 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) // true
console.log(bird.canFly) // true
bird.fly() // 我正在飛行
const penguin = new Penguin()
console.log(penguin.hasWings) // true
console.log(penguin.canFly) // false
penguin.fly() // 我不能飛行