关於多型

什麽是多型

In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.

在程序语言中,「多型」代表提供不同类型的实体一个统一的介面,或使用一个单一的符号来表示多个不同的类型。

Polymorphism is the ability of an object to take on many forms.

「多型」是一个物件可以拥有多种型态的方式

短短的解释,但却很难让人理解他的真正意义。实际上「多型」本身有许多不同的实作方式,不如就我们就先来看看一些实际的例子吧

特设多型 Ad hoc polymorphism

特设多型主要描述的是同一个方法需要处理多种 types 的时候,譬如在 JavaScript 当中,+ 可以处理 number 和 string type 的资料,这里的 + 表现的就是特设多型。

1 + 2 = 3
'1' + '2' = '12'

所以如果我们自己写了一个方法,可以处理不同 types 的话,就是在展性特设多型的特性,像是下面的 cook 方法,可以传入肉类或蔬菜类,最後产出一道菜

type Meat {
  protein: xxx
}

type Vege {
  nutrition: xxx
  fiber: xxx
}

type Dish {
 flavor: xxx
}

const cook = (
  firstInput: Meat | Vege,
  secondInput: Meat | Vege
): Dish => {
  // ....
}

参数多型 Parametric polymorphism

在刚刚的特设多型里面,我们可以处理多个已知的 types,但如果遇到不知道 type 的状况该怎麽办呢?这时候我们可以把 type 当作是一个参数并动态做使用。

以下面的例子来看,有一个 getProperty 方法,当我们输入 key 的时候,会回传物件当中相对应的值。

function getProperty(obj: object, key: string) {
  return obj[key]
}

不过我们希望限制输入的 key 的 types,譬如

type Key = 'xxx' | 'yyy'

function getProperty(obj: object, key: Key) {
  return obj[key]
}

然而每个物件的 keys 都不一样,所以我们无法真正写下 type Key 当中的内容,但是我们可以根据传入的物件的 type 来动态指定 Key 的内容,像是

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key]
}

这里的意思是,将 type Key 设定为传入的物件当中的所有 keys。所以当我们传入下面的 foo 物件之後,这时 type Key 就会是 'a' | 'b' | 'c' | 'd'

const foo = { a: 1, b: 2, c: 3, d: 4 }
getProperty(foo, "a")    // 1 , Key = 'a' | 'b' | 'c' | 'd'

如果我们输入了非指定的 key type,那麽就会出现错误,而不是回传 undefined

const foo = { a: 1, b: 2, c: 3, d: 4 }
getProperty(foo, "z")    // type error

子类型 Subtyping

在另外一种状况下,我们会希望方法可以支援多种 types,不过我们还是得为这个方法的输入设定 type。如果要想到所有可能的 type 可能没有办法,那麽,我们可以只设定 parent type (or super type),然後让这个方法同样可以接受所有的 child type (or subtype) 吗?

许多程序语言有支援「子类型 Subtyping」这种多型的实作。以下面的例子来说,BaseballPlayer type 完整包含了 Person type,所以虽然 getName 方法定义输入的 type 是 Person,但我们仍然可以放入 BaseballPlayer type 的物件,并顺利得到结果

interface Person {
  name: string;
}

interface BaseballPlayer {
  name: string;
  team: string;
}
  
const getName = (person: Person): string => {
  retrun person.name
}

const ohtani: BaseballPlayer = {
  name: 'Shohei Ohtani',
  team: 'Angels'
}
 
getName(ohtani)   // Shohei Ohtani

刚刚提到 BaseballPlayer type 完整包含了 Person type,其实也就等同於 BaseballPlayer type 继承了 Person type

interface Person {
  name: string;
}

interface BaseballPlayer extends Person {
  team: string;
}

鸭子型别 Duck typing

鸭子型别,这个名称相当特殊的一种多型实现方式,它源自於 Duck Test:

"If it walks like a duck and it quacks like a duck, then it must be a duck"

意思就是不管前面这只动物是属於哪一类,只要他走路像鸭子、叫声像鸭子,那麽我们就可以认定他是只鸭子。

With normal typing, suitability is determined by an object's type. In duck typing, an object's suitability is determined by the presence of certain methods and properties, rather than the type of the object itself.

所以先前各种实现多型的情况下,我们会关注物件的型别,来决定该如何与物件互动。但在鸭子型别的状况下,我们只关注物件的行为,只要他的行为如我们所预期,那麽就可以与他互动。

举例来说,我们建立了一个 askSomeoneToRun 方法,专门负责叫人开始跑。不过这里并没有限定传入的物件型别,而是只要传入的物件本身有 run 方法,那麽就可以执行。也就是说,我们不关注物件的型别,只关注他本身的行为。

class BaseballPlayer {
  constructor() {}

  hit(): void {
    console.log('hit like a baseball player')
  }
  run(): void {
    console.log('run like a baseball player')
  }
}

class FootballPlayer {
  constructor() {}
  
  shot(): void {
    console.log('shot like a football player')
  }

  run(): void {
    console.log('run like a football player')
  }
}

const askSomeoneToRun = (someone: any) => {
  someone.run()
}

const jeter = new BaseballPlayer()
const neymar = new FootballPlayer()

askSomeoneToRun(jeter)      // run like a baseball player
askSomeoneToRun(neymar)     // run like a football player
 

所以到底什麽是多型

在物件导向的世界,我们用类别 (class) 和型别 (type) 来区别各种不同的物件,而许多时候我们的方法只会针对一种类别或型别来操作,但如果我们想要同时操作多种不同的类别和型别,那麽就需要用一些不同的方式来实现,这些不同的方式所实现的就是「多型」。

有了「多型」的概念和实现能力,我们可以让程序码本身具备更多的拓展性和重复使用性。


<<:  鬼故事 - 印表机最终还是挂了

>>:  Day9 NiFi - Controller Service

【CCNA-LAB】设定VLAN 重点纪录

单纯为记录上课时学到的命令语句, 设定VLAN目前最为不熟悉((在教到的东西中 忘记时,随时可以回来...

D26 将config等等隐密资讯另外放置 - yaml

将重要资讯放到yaml内 config.yaml(放在BASE_DIR) --- email: EM...

4.MYSQL语法简介

在SQL中写法很重要,因为写法会影响着最後输出的结果 如果我们要写出自己的资料库就一定要先了解他们的...

第38天~画完图来看语法

这篇的上一篇:https://ithelp.ithome.com.tw/articles/10283...

< 关於 next.js: 开始打地基| Next中的Pages,究竟有什麽用途? >

09-15-2021 本章内容 pages意想不到的用途! 每个页面都是以pages作为基准路径 动...