什麼是繼承

In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation. Also defined as deriving new classes (sub classes) from existing ones such as super class or base class and then forming them into a hierarchy of classes.

在物件導向程式語言當中,「繼承」是一種機制,可以保留某個類別或物件的實作方法。奠基在既有類別上創立一個新的類別,並建立階層的關係。

In most class-based object-oriented languages, an object created through inheritance, a "child object", acquires all the properties and behaviors of the "parent object"
...
Inheritance allows programmers to create classes that are built upon existing classes, to specify a new implementation while maintaining the same behaviors (realizing an interface), to reuse code and to independently extend original software via public classes and interfaces. The relationships of objects or classes through inheritance give rise to a directed graph.

child 物件繼承 parent 物件時,可以

  • 獲得了 parent 物件的所有屬性和方法
  • 用新的方式實現、但同時維持繼承而來的 parent 物件的原有行為
  • 重複使用或延伸原有的程式碼

拿先前提到的例子來看,並做一些延伸如下:

class BaseballPlayer {
  name: string

  constructor(name: string) {
    this.name = name
  }

  hit() {
    console.log(`${this.name} can hit`)
  }

  run() {
    console.log(`${this.name} can run`)
  }
}

class Shortstop extends BaseballPlayer {
  run() {
    console.log(`${this.name} can run very fast`)
  }

  defense() {
    console.log(`${this.name} can defense`)
  }
}
const jeter = new Shortstop('jeter')

jeter.name         // jeter
jeter.hit()        // jeter can hit
jeter.defense()    // jeter can can defense
jeter.run()        // jeter can run very fast

可以看到

  • Shortstop 類別繼承了 BaseballPlayer 類別
  • Shortstop 類別的實例 jeter,擁有其 parent 類別的
    • name 屬性
    • hit 方法,並維持同樣的實作方式
    • run 方法,但使用了新的實作方式
  • Shortstop 類別的實例 jeter 擁有新的 defense 方法

為什麼要繼承

其實「抽象」和「繼承」是一體兩面。當我們不斷的將「許多類別」的共同特質抽出、變成更高一層的 parent 類別的時候(抽象),這時候「許多類別」就是繼承了這個 parent 類別,也就是繼承了這個共同特質。

在能夠繼承的情況下,因為可以直接使用 parent 類別、parent's parent 類別 ... 等的屬性和方法,因此 child 類別來說,可以不需要重複撰寫同樣的程式碼。另一方面,「繼承」也建立了物件之間彼此的階層關係,讓我們能夠更快速的模擬真實世界事物的關係。

雖然「繼承」看起還有許多好處,不過「繼承」的缺點是,parent 類別和 child 類別有高度的關聯性,換個角度來說,就是難以根據新的需求或變化進行擴充。

譬如「人」這個類別只會在地上跑,不會在天上飛。如果今天我們想要建立一個超人的實例,那麼究竟是要讓「超人」類別去繼承「人」呢,還是繼承「鳥」類別呢?在這樣的情況下,先前所提到過抽象了另外一種實現方式「介面」就可以派上用場了!

class Human {
  name: string

  constructor(name: string) {
    this.name = name
  }

  run(): void {
    console.log(`${this.name} can run`)
  }
}

interface Fly {
  fly(): void;
}

class Superman extends Human implements Fly {
  fly(): void {
     console.log(`${this.name} can fly`)
  }
}

const clark = new Superman('clark')
clark.run()                          // clark can run
clark.fly()                          // clark can fly

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


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







Related Posts

建立屬於你的 Google Map 地圖標記(二) - 渲染自定參數的地圖元件

建立屬於你的 Google Map 地圖標記(二) - 渲染自定參數的地圖元件

CSS 的模組化(OOCSS、SMACSS、BEM)

CSS 的模組化(OOCSS、SMACSS、BEM)

[day-3]布林、undefined、null/字串數字轉換/比較與邏輯運算子

[day-3]布林、undefined、null/字串數字轉換/比較與邏輯運算子


Comments