OOP - 14 Liskov Substitution Principle


Posted by tsungtingdu on 2021-09-30

今天來談談 SOLID 當中的里氏替換原則,同樣的先來看一下例子。

延續先前的例子,公司持續拓展,滿足更多不同使用者的需求。現在公司決定,讓使用者可以在建立實例之後,能夠透過一些方法來調整形狀的屬性。以 Rectangle 來說,新增了 setWidthsetLength 方法分別調整寬和長。

class Rectangle implements Shape {
  width: number
  length: number

  constructor(width: number, length: number) {
    this.width = width
    this.length = length
  }

  setWidth(input: number): void {
    this.width = input
  }

  setLength(input: number): void {
    this.length = input
  }

  getArea(): number {
    return this.width * this.length
  }
}

所以如果原本建立 rectangle 的時候,設定長和寬為 7 和 7,之後我們可以改為長 11 寬 13,最後會得到面積 143

const rectangle = new Rectangle(7, 7)
rectangle.getArea()                     // 49
rectangle.setLength(11)
rectangle.setWidth(13)
rectangle.getArea()                    // 143

這時候部門裡面有人看到上面的例子之後,有個突發奇想,想要讓使用者可以同時修改長和寬。於是就建立了一個繼承 Rectangle 的新類別 NewRectangle。而因為要能夠實現剛剛的想法,所以也調整了原本 setLengthsetWidth 的實作方式

class NewRectangle extends Rectangle {
  constructor(size: number) {
    super(size, size)
  }

  setWidth(input: number): void {
    this.width = this.length = input
  }

  setLength(input: number): void {
    this.width = this.length = input
  }
}

結果的確能夠順利實作出這個新的類別,並且同樣能夠計算出面積。

const newRectangle = new NewRectangle(7)
newRectangle.getArea()                       // 49

這時客戶看到有新的類別,就躍躍欲試的使用,當設定完長 11 寬 13 之後,本來以為會跟 Rectangle 一樣,得到面積 143,但沒想到這時候卻得到結果 169

newRectangle.setLength(11)
newRectangle.setWidth(13)
newRectangle.getArea()                       // 169

客戶沒有得到期待中的結果,就找公司抱怨說:「為什麼用了你們家新版本的產品,卻沒有得到原本的結果?」

於是公司就回頭跟各部門說:「你們要怎麼更新版本或是改變實作方式都沒關係,但是滿足客戶同樣的期待」

里氏替換原則

於是,公司內部就出現了一條新規則。

Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program.

在物件導向程式設計當中,里氏替換原則指出,如果 S 是 T 的子類別,,那麼 S 就需要能夠取代 T 而不影響其他方面的執行。以剛剛的例子來說,當NewRectangle 繼承了 Rectangle,那麼就會期待使用者可以用 NewRectangle 來取代 Rectangle,並看到期待中的結果。

從使用者角度出發

先前提到的「單一功能原則」、「開放封閉原則」、「依賴反轉原則」,談的都是當「外在」需求變動的時候,程式「內部」可以用最少的力氣去修正與擴充,以滿足新的需求。

但隨著時間演進,程式內部可能會有新的實作方式、新的版本出現,但不管內部怎麼調整,對外部的使用者來說,應該都要維持同樣的期待。如果無法維持同樣的期待,那麼我們就需要花額外的力氣去通知使用者調整使用方法或期待。


鐵人賽發表網址:幫自己搞懂物件導向和設計模式


#OOP #Object-oriented programming #SOLID #TypeScript #2021-ironman







Related Posts

MTR04_0711

MTR04_0711

Functional Programming 筆記

Functional Programming 筆記

淺談產品開發與工作流程

淺談產品開發與工作流程


Comments