物件導向名詞

簡介物件導向的名詞,以 TypeScript 為例。

種類

介紹各種你會看到的名詞。

類別

Class,類別,一種可以整合邏輯和狀態的單位,例如:

// 類別名稱,貴賓狗 class Poodle { // 靜態公開屬性,正常的貴賓狗的腿數 public static legs = 4; // 公開屬性,這個貴賓狗的腿數 public legs: number; // 私有屬性,這個貴賓狗的能量 private energy: number; // 建構子 constructor(legs = Poodle.legs, energy = 10) { this.legs = legs; this.energy = energy; } // 公開函式,貴賓狗呼叫 public shout(): string { this.shoutCost(); return 'woof'; } // 公開函式,這隻貴賓狗受傷了嗎 public isInjured() { return Poodle.legs > this.legs; } // 公開函式,這隻貴賓狗是不是累了 public isTired(): boolean { return this.energy < 5; } // 私有函式,當叫了之後會消耗的東西 private shoutCost(): void { this.energy -= 3; } }

接著你可以這樣操作:

// 建構實例,常說 new 一個 instnace const poodle = new Poodle(3); // 使用公開函式 console.log(`Is injured? ${poodle.isInjured()}`); console.log(`Shout: ${poodle.shout()}`); console.log(`Is tired? ${poodle.isTired()}`); console.log(`Shout: ${poodle.shout()}`); console.log(`Is tired? ${poodle.isTired()}`); // 使用公開屬性 console.log(`Legs should have: ${Poodle.legs}, but get ${poodle.legs}`); // 以下操作會出錯 poodle.energy; poodle.shoutCost();

有幾點名詞:

  • new(construct),建構,建構出來的會稱為一個實例(instance)或物件(object)
  • attribute(property),屬性,實例專屬的值
  • method(function),函式,實例專屬的函式
  • public,公開,外部操作實例可以用的函式,也可以在類別內部使用
  • private,私有,只有類別內部可以使用

抽象類別

把類別抽象化,例如:

abstract class Dog { public static legs = 4; public legs: number; // 注意這裡從 private 改成 protected protected energy: number; constructor(legs = Dog.legs, energy = 10) { this.legs = legs; this.energy = energy; } public shout(): string { this.shoutCost(); return 'woof'; } public isInjured(): boolean { return Dog.legs > this.legs; } // 注意這裡從 private 改成 protected // 除此之外,把這個函示抽象化,abstract protected abstract shoutCost(): void; } // 貴賓犬 class Poodle extends Dog { protected shoutCost(): void { this.energy -= 3; } } // 鬥牛犬 class Bulldog extends Dog { // 呆呆的狗種,嚎叫會消耗更多體力 protected shoutCost(): void { this.energy -= 4; } }

這裡有幾個新的名詞:

  • extends(inherit),繼承,PoodleBulldog 都去繼承抽象類別 Dog
  • protected,保護,除了類別內部使用,繼承後的類別,也可以使用這個種類的屬性或函式
  • abstract,抽象,把類別、函式或屬性抽象,繼承的類別必須去實作(implement)他

但是抽象類別不能建構:

// 會出錯 const dog = new Dog();

介面

除了抽象類別,你也可以用介面來把抽象層度拉高:

interface Animal { legs: number; isInjured(): boolean; } // 注意這裡是用 implements 不是 extends abstract class Dog implements Animal {}
  • interface,介面,一種契約,會要求實作(implements)它的(抽象)類別去實作某些函式或屬性,只需定義公開函式或屬性

介面一樣不能建構:

// 會出錯 const animal = new Animal();

這裡再強調一下介面本身沒有實作,介面只是告訴大家:我有這個函式,但其他人怎麼實作的我不知道。以上面的介面為例,所有 Animal 都可以有 isInjured 這個函示,並且他回傳的值必須是 boolean

也因此類別必須去「實作」這個介面,以上面的抽象類別 Dog 為例,他就實作了這個函示:

abstract class Dog implements Animal { public isInjured(): boolean { // 這裡的程式碼,稱為實作 return Dog.legs > this.legs; } }

比較一下

  • 建構(construct, new)和 實作(implements)的差別
  • 私有(private)、保護(protected)、公開(public) 的差別
  • 介面(interface) 和 抽象類別(abstract class) 的差別
種類 建構 實作 抽象層度
class O O
abstract class X O
interface X X

為什麼要拉高抽象層度?

想像一下朋友打電話給你,問你在幹嘛,你可以有兩種選擇:

  • 我在吃飯
  • 我在用銀色湯匙裝著約三克、六十顆的米飯,並用右手抓著湯匙,正準備送進嘴巴並咀嚼(然後你就失去你唯一的朋友了)

抽象之後,就可以延伸很多設計模式(Design Patterns)。

整合一下

雖然介面和抽象類別不能建構,但是他可以被用作型別(type)。

function influriate(animal: Animal) { return animal.shout(); } const poodle = new Poodle(); console.log(influriate(poodle));