| 多态 |
| --- |
| 特设多态 |
| * 函数重载
* 运算符重载
 |
| 參數多态 |
| * 泛化函数
* 泛型编程
 |
| 子类型 |
| * 虛函數
* 單一與動態分派
* 多分派
* 雙分派（英语：）
 |
| * 
* 论
* 编
 |


|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
编程范型 |
| --- |
|  |
| 指令式 | 
* 过程式
* 块结构
* 例外处理
* 结构化
* 模块化

 |
|  |
| 函数式 | 
* 头等函数
* 纯函数式
* 隐式
* 模式匹配
* 推导式

 |
|  |
| 面向对象 | 
* 基于类
* 基于原型
* 契约式
* 面向方面
* 面向代理

 |
|  |
| 数据流程 | 
* 同步式
* 响应式
	+ 函数式响应式
* 串流处理
* 基于流程

 |
|  |
| 宣告式 | 
* 逻辑式
	+ 回答集
* 约束式
* 数据查询
* 框架本体

 |
|  |
|  | 
* 协程
* 演员模型
* 通信顺序进程
* future/promise
* 分叉会合
* 整体同步
* 映射归约

 |
|  |
| 其他 | 
* 递归
* 阵列式
* 多态
	+ 运算符重载
	+ 泛型
	+ 多分派
* 面向堆栈
* 非确定性
* 数据驱动
* 事件驱动
* 自动机
* 可微分
* 概率式

 |
|  |
| 按语言比较（英语：） |

 |

**多分派**或译**多重派发**（multiple dispatch）或**多方法**（multimethod），是某些编程语言的一个特性，其中的函数或者方法，可以在运行时间（动态的）基于它的实际参数的类型，或在更一般的情况下于此之外的其他特性，来动态分派。这是对单分派多态的推广， 那里的函数或方法调用，基于在其上调用方法的对象的派生类型，而动态分派。多分派使用一个或多个实际参数的组合特征，路由动态分派至实现函数或方法。

理解分派
----

软件工程师通常把代码写进代码块中，代码块通常称作过程、函数、方法。代码通过被调用来执行，调用时将控制权传入函数中，当函数执行完成后将控制权返回给调用者。

函数名通常用来描述函数的目的。有时会将多个函数起同样的名称。比如同名函数在逻辑上处理相同的任务，但是操作在不同类型的输入值上。在这种情况下，无法仅仅通过函数名，来判断目标代码块。那么，函数的实际参数的个数和类型，也就被用来判断。

通常，单分派面向对象语言，在调用一个方法时，方法参数中一个参数会被特殊对待，并用来决定哪一个方法（如果有多个同名方法）会被调用。在许多语言中，这个特殊的参数是在语法上指明的，许多编程语言在调用方法时，把特殊参数放在小圆点（`.`）之前。例如 `special.method(other, arguments, here)`，这样 `lion.sound()` 将会发出狮吼，同时 `sparrow.sound()` 只会吱吱地叫。一般来说，对于面向对象的编程语言，这个小圆点之前的参数（上例中的`lion`和`sparrow`）被称为接收者。

相反，在实现了多分派的语言中，被调用的函数，即是那些参数个数一样多，并且类型也匹配的调用。在调用中并没有特殊参数，来决定那个方法被调用。也就是说，所有参数的运行时类型都参与分派。CLOS是早期和著名的多分派语言。

### 数据类型

对于编译时间可以区分数据类型的编程语言，在交替（英语：）（alternative）函数中进行选择，可以发生在编译时间，创建交替函数用于编译时间选择的活动，通常被叫做函数重载。

在有些编程语言中，这种数据类型的识别，可以被延后至运行时间（后期绑定（英语：））。交替函数的选择发生在运行时间，并依据动态确定的函数实际参数的类型。以这种方式选择交替实现的函数，通常被称为多方法。

例子
--

可以通过例子更加清晰的区分多分派和单一分派。假想一个游戏，它有两种（用户可见的）物体：飞船和小行星。当两个物体要相撞的时候，程序需要依据什么物体要相撞而做不同的事情。

### 具有内建多分派的语言

#### Common Lisp

在具有多分派的Common Lisp语言中，可以在Common Lisp对象系统中如下这样实现：

```
(defgeneric collide (x y))
(defclass asteroid () ())
(defclass spaceship () ())
(defmethod collide-with ((x asteroid) (y asteroid))
 ;; deal with asteroid hitting asteroid
 )
(defmethod collide-with ((x asteroid) (y spaceship))
 ;; deal with asteroid hitting spaceship
 )
(defmethod collide-with ((x spaceship) (y asteroid))
 ;; deal with spaceship hitting asteroid
 )
(defmethod collide-with ((x spaceship) (y spaceship))
 ;; deal with spaceship hitting spaceship
 )

```

并且对其他方法也是类似的。没有使用显式测试和"动态转换"。

由于多分派的存在，方法要定义在类中并包含在对象中的传统想法，变得不再吸引人了，上述每个`collide-with`方法，都附属于两个不同的类而非一个类。因此方法调用的特殊语法，一般会消失，从而方法调用看起来完全就像正常的函数调用，并且方法被组织入泛化函数而非类中。

#### Julia

Julia有内建的多分派，并且它是语言设计的中心。Julia版本的例子如下：

```
collide\_with(x::Asteroid, y::Asteroid) = ... # deal with asteroid hitting asteroid
collide\_with(x::Asteroid, y::Spaceship) = ... # deal with asteroid hitting spaceship
collide\_with(x::Spaceship, y::Asteroid) = ... # deal with spaceship hitting asteroid
collide\_with(x::Spaceship, y::Spaceship) = ... # deal with spaceship hitting spaceship

```

#### C#

C#在版本4（2010年4月），使用关键字`dynamic`，介入了对动态多方法的支持。下面的例子展示多方法协同于在版本8（2019年9月）中介入的`switch`表达式。像很多其他静态类型的语言语言一样，C#还支持静态方法重载，Microsoft预期开发者在多数场景下会选用静态类型超过动态类型。`dynamic`关键字支持COM对象和动态类型的.NET语言的互操作。

```
class Program
{
 static void Main()
 {
 Console.WriteLine(Collider.Collide(new Asteroid(101), new Spaceship(300)));
 Console.WriteLine(Collider.Collide(new Asteroid(10), new Spaceship(10)));
 Console.WriteLine(Collider.Collide(new Spaceship(101), new Spaceship(10)));
 }
}

static class Collider
{
 public static string Collide(SpaceObject x, SpaceObject y) =>
 ((x.Size > 100) && (y.Size > 100)) ?
 "Big boom!" : CollideWith(x as dynamic, y as dynamic);
 private static string CollideWith(Asteroid x, Asteroid y) => "a/a";
 private static string CollideWith(Asteroid x, Spaceship y) => "a/s";
 private static string CollideWith(Spaceship x, Asteroid y) => "s/a";
 private static string CollideWith(Spaceship x, Spaceship y) => "s/s";
}

abstract class SpaceObject
{
 public SpaceObject(int size) => Size = size;

 public int Size { get; }
}

class Asteroid : SpaceObject
{
 public Asteroid(int size) : base(size) { }
}

class Spaceship : SpaceObject
{
 public Spaceship(int size) : base(size) { }
}

```

输出：

```
big-boom
a/s
s/s

```

#### Groovy

Groovy是通用的Java兼容/互用的JVM语言，它对立于Java，使用后期绑定/多分派。

```
/\*
 Groovy implementation of C# example above
 Late binding works the same when using non-static methods or compiling class/methods statically
 (@CompileStatic annotation)
\*/
class Program {
 static void main(String[] args) {
 println Collider.collide(new Asteroid(101), new Spaceship(300))
 println Collider.collide(new Asteroid(10), new Spaceship(10))
 println Collider.collide(new Spaceship(101), new Spaceship(10))
 }
}

class Collider {
 static String collide(SpaceObject x, SpaceObject y) {
 (x.size > 100 && y.size > 100) ? "big-boom" : collideWith(x, y) // Dynamic dispatch to collideWith method
 }

 private static String collideWith(Asteroid x, Asteroid y) { "a/a" }
 private static String collideWith(Asteroid x, Spaceship y) { "a/s" }
 private static String collideWith(Spaceship x, Asteroid y) { "s/a" }
 private static String collideWith(Spaceship x, Spaceship y) { "s/s"}
}

class SpaceObject {
 int size
 SpaceObject(int size) { this.size = size }
}

@InheritConstructors class Asteroid extends SpaceObject {}
@InheritConstructors class Spaceship extends SpaceObject {}

```

### 用多分派库扩展的语言

在不于语言定义或语法层次支持多分派的语言中，可能经常使用库扩展来增加多分派。

#### JavaScript

JavaScript和TypeScript不在语言语法层次上支持多方法，但可以通过库来增加多分派。例如，使用multimethod包，它提供了多分派、泛化函数的实现。JavaScript的动态类型版本：

```
import { multi, method } from '@arrows/multimethod'

class Asteroid {}
class Spaceship {}

const collideWith = multi(
 method([Asteroid, Asteroid], (x, y) => {
 // deal with asteroid hitting asteroid
 }),
 method([Asteroid, Spaceship], (x, y) => {
 // deal with asteroid hitting spaceship
 }),
 method([Spaceship, Asteroid], (x, y) => {
 // deal with spaceship hitting asteroid
 }),
 method([Spaceship, Spaceship], (x, y) => {
 // deal with spaceship hitting spaceship
 }),
)

```

TypeScript有对应的静态类型版本。

#### Python

可以使用库扩展来向Python增加多分派。例如，最早的模块multimethods.py，它为Python提供了CLOS风格的多方法而不用变更语言的底层语法或关键字。在功能上，这非常类似于CLOS例子，但是语法是常规Python的。
Guido van Rossum使用Python 2.4介入的修饰器（decorator），出品了多方法的具有简化了的语法的一个简单实现，他为此定义了`multimethod`修饰器。
multipledispatch采用的形式与之一致。

模块multimethod，采用了修饰器和Python 3.5介入的类型提示实现多方法：

```
from multimethod import multimethod

class Asteroid(): pass

class Spaceship(): pass

@multimethod
def collide\_with(x: Asteroid, y: Asteroid):
 '''deal with asteroid hitting asteroid'''
    print("asteroid hitting asteroid")

@multimethod
def collide\_with(x: Asteroid, y: Spaceship):
 '''deal with asteroid hitting spaceship'''
    print("asteroid hitting spaceship")

@multimethod
def collide\_with(x: Spaceship, y: Asteroid):
 '''deal with spaceship hitting asteroid'''
    print("spaceship hitting asteroid")

@multimethod
def collide\_with(x: Spaceship, y: Spaceship):
 '''deal with spaceship hitting spaceship'''
    print("spaceship hitting spaceship")

```

```
>>> a = Asteroid()
>>> b = Spaceship()
>>> collide\_with(a, b)
asteroid hitting spaceship

```

此外，还有PEAK-Rules包提供语法类似上述例子的多分派，它后来被替代为PyProtocols。Reg库也支持多分派和谓词分派。

#### C

C语言使用C Object System库，可以支持类似于CLOS的动态分派。它是完全可扩展的并且方法不需要任何的手工处理。动态消息（方法）通过COS分派器来分派，它比Objective-C更快。下面是使用COS的例子：

```
#include <stdio.h>
#include <cos/Object.h>
#include <cos/gen/object.h>

/\* 类 \*/
defclass (Asteroid)
/\* 数据成员 \*/
endclass

defclass (Spaceship)
/\* 数据成员 \*/
endclass

/\* 泛化函数 \*/
defgeneric (\_Bool, collide\_with, \_1, \_2);

/\* 多方法 \*/
defmethod (\_Bool, collide\_with, Asteroid, Asteroid)
 /\* deal with asteroid hitting asteroid \*/
endmethod

defmethod (\_Bool, collide\_with, Asteroid, Spaceship)
 /\* deal with asteroid hitting spaceship \*/
endmethod

defmethod (\_Bool, collide\_with, Spaceship, Asteroid)
 /\* deal with spaceship hitting asteroid \*/
endmethod

defmethod (\_Bool, collide\_with, Spaceship, Spaceship)
 /\* deal with spaceship hitting spaceship \*/
endmethod

/\* 用例 \*/
int main(int argc, char \*argv[])
{
 OBJ a = gnew(Asteroid);
 OBJ s = gnew(Spaceship);

 printf("<a,a> = %d\n", collide\_with(a, a));
 printf("<a,s> = %d\n", collide\_with(a, s));
 printf("<s,a> = %d\n", collide\_with(s, a));
 printf("<s,s> = %d\n", collide\_with(s, s));

 grelease(a);
 grelease(s);
}

```

编程语言支持
------

### 主范型

* Julia

### 支持通用的多方法

* C# 4.0（英语：）
* Cecil（英语：）
* Clojure
* Common Lisp（通过Common Lisp对象系统）
* Dylan
* Fortress（英语：）
* Groovy
* Lasso（英语：）
* Nim，在v0.20之后泛化方法被废止，使用多方法需要加编译指令
* Raku
* R
* Seed7（英语：）
* TADS（英语：）
* VB.Net，通过后期绑定，还通过.Net DLR
* Wolfram语言，通过符号模式匹配
* Xtend（英语：）

### 通过扩展

* 任何.NET语言（通过库MultiMethods.NET）
* C（通过库C Object System）
* C#（通过库multimethod-sharp）
* C++（通过库yomm2和multimethods）
* D（通过库openmethods）
* Factor（通过标准multimethods词汇表)
* Java（使用扩展MultiJava）
* JavaScript（通过包@arrows/multimethod）
* Perl（通过模块Class::Multimethods）
* Python（模块multimethod、multipledispatch或Reg库）
* Racket（通过库multimethod-lib）
* Ruby（通过Multiple Dispatch库、Multimethod包和Vlx-Multimethods包）
* Scheme（通过TinyCLOS）
* TypeScript（通过包@arrows/multimethod）

模拟多分派
-----

### C

C语言没有动态分派，也可以不使用C Object System库，而以某种形式手工实现。动态分派经常使用`enum`来标识一个对象的子类型，然后可通过在函数指针分支表（英语：）中查找这个值来完成。C语言模拟多方法的简单例子：

```
typedef void (\*CollisionCase)(void);

void collision\_AA(void) { /\* handle Asteroid-Asteroid collision \*/ };
void collision\_AS(void) { /\* handle Asteroid-Spaceship collision \*/ };
void collision\_SA(void) { /\* handle Spaceship-Asteroid collision \*/ };
void collision\_SS(void) { /\* handle Spaceship-Spaceship collision\*/ };

typedef enum {
 THING\_ASTEROID = 0,
 THING\_SPACESHIP,
 THING\_COUNT /\* not a type of thing itself, instead used to find number of things \*/
} Thing;

CollisionCase collisionCases[THING\_COUNT][THING\_COUNT] = {
 {&collision\_AA, &collision\_AS},
 {&collision\_SA, &collision\_SS}
};

void collide(Thing a, Thing b) {
 (\*collisionCases[a][b])();
}

int main(void) {
 collide(THING\_SPACESHIP, THING\_ASTEROID);
}

```

### Java

在只有单一分派的语言比如Java中，多分派可以用多层单一分派来模拟：

```
interface Collideable {
 void collideWith(final Collideable other);

 /\* These methods would need different names in a language without method overloading. \*/
 void collideWith(final Asteroid asteroid);
 void collideWith(final Spaceship spaceship);
}

class Asteroid implements Collideable {
 public void collideWith(final Collideable other) {
 // Call collideWith on the other object.
 other.collideWith(this);
 }

 public void collideWith(final Asteroid asteroid) {
 // Handle Asteroid-Asteroid collision.
 }

 public void collideWith(final Spaceship spaceship) {
 // Handle Asteroid-Spaceship collision.
 }
}

class Spaceship implements Collideable {
 public void collideWith(final Collideable other) {
 // Call collideWith on the other object.
 other.collideWith(this);
 }

 public void collideWith(final Asteroid asteroid) {
 // Handle Spaceship-Asteroid collision.
 }

 public void collideWith(final Spaceship spaceship) {
 // Handle Spaceship-Spaceship collision.
 }
}

```

运行时间`instanceof`检查可以在一个或两个层次上使用。

代码示例
----

1. ↑  
TypeScript的多方法示例：

```
import { multi, method, Multi } from '@arrows/multimethod'

class Asteroid {}
class Spaceship {}

type CollideWith = Multi & {
 (x: Asteroid, y: Asteroid): void
 (x: Asteroid, y: Spaceship): void
 (x: Spaceship, y: Asteroid): void
 (x: Spaceship, y: Spaceship): void
}

const collideWith: CollideWith = multi(
 method([Asteroid, Asteroid], (x, y) => {
 // deal with asteroid hitting asteroid
 }),
 method([Asteroid, Spaceship], (x, y) => {
 // deal with asteroid hitting spaceship
 }),
 method([Spaceship, Asteroid], (x, y) => {
 // deal with spaceship hitting asteroid
 }),
 method([Spaceship, Spaceship], (x, y) => {
 // deal with spaceship hitting spaceship
 }),
)

```
2. ↑  
Python的multimethods.py示例：

```
from multimethods import Dispatch
from game\_objects import Asteroid, Spaceship
from game\_behaviors import as\_func, ss\_func, sa\_func
collide = Dispatch()
collide.add\_rule((Asteroid, Spaceship), as\_func)
collide.add\_rule((Spaceship, Spaceship), ss\_func)
collide.add\_rule((Spaceship, Asteroid), sa\_func)
def aa\_func(a, b):
 """Behavior when asteroid hits asteroid."""
    # ...define new behavior...
collide.add\_rule((Asteroid, Asteroid), aa\_func)

```

```
# ...later...
collide(thing1, thing2)

```
3. ↑  
Python的van Rossum最初的多方法实现：

```
@multimethod(Asteroid, Asteroid)
def collide(a, b):
 """Behavior when asteroid hits a asteroid."""
    # ...define new behavior...
@multimethod(Asteroid, Spaceship)
def collide(a, b):
 """Behavior when asteroid hits a spaceship."""
    # ...define new behavior...
# ... define other multimethod rules ...

```

引用
--

1. ↑  Ranka, Sanjay; Banerjee, Arunava; Biswas, Kanad Kishore; Dua, Sumeet; Mishra, Prabhat; Moona, Rajat.. Springer. 2010-07-26  [2021-03-23]. ISBN 9783642148248. （原始内容存档于2021-04-27）.
2. ↑  Igor Wojda.. Kt.Academy. 2018-02-08  [2020-02-27] （英语）.
3. ↑  Bezanson, Jeff; Edelman, Alan; Karpinski, Stefan; Shah, Viral B.. SIAM Review. 7 February 2017, **59** (1): 65–98. arXiv:1411.1607 可免费查阅. doi:10.1137/141000671.
4. ↑ .  [2020-05-14]. （原始内容存档于2021-05-26）.
5. ↑ .  [2020-05-14]. （原始内容存档于2021-06-28）.
6. ↑ .  [2020-05-14]. （原始内容存档于2021-04-16）.
7. ↑ .  [2020-05-14]. （原始内容存档于2021-05-24）.
8. ↑ .  [2021-04-15]. （原始内容存档于2021-04-16）.
9. 1 2 3  @arrows/multimethod （页面存档备份，存于） Multiple dispatch in JavaScript/TypeScript with configurable dispatch resolution by Maciej Cąderek.
10. ↑  multimethods.py （页面存档备份，存于）, Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al.
11. ↑ .  [2014-07-13]. （原始内容存档于2021-05-29）.
12. 1 2 .  [2021-04-15]. （原始内容存档于2020-11-11）.
13. 1 2  Coady, Aric,  [2021-01-28], （原始内容存档于2020-12-31）
14. ↑ . Python Package Index.  [21 March 2014]. （原始内容存档于2017-03-14）.
15. ↑ . Python Enterprise Application Kit.  [26 April 2019]. （原始内容存档于2021-05-05）.
16. 1 2 . Read the docs.  [26 April 2019]. （原始内容存档于2021-03-05）.
17. 1 2 . 2019-02-19  [2021-03-30]. （原始内容存档于2021-05-01）.
18. ↑ . The Julia Manual. Julialang.  [11 May 2014]. （原始内容存档于2016-07-17）.
19. ↑ .  [2009-08-20]. （原始内容存档于2009-08-25）.
20. ↑ .  [2008-04-13]. （原始内容存档于2016-09-01）.
21. ↑ .  [2008-09-04]. （原始内容存档于2015-09-20）.
22. ↑  Steele, Guy L.. . Bedford, MA, U.S.A: Digital Press. 1990  [2021-03-30]. ISBN 978-1-55558-041-4. （原始内容存档于2017-12-17）.
23. ↑ .  [2008-04-13]. （原始内容存档于2020-04-04）.
24. ↑   (PDF).  [2010-04-23]. （原始内容 (PDF)存档于2013-01-20）.
25. ↑ .  [2008-04-13]. （原始内容存档于2011-08-12）.
26. ↑ .  [2014-11-11]. （原始内容存档于2021-06-13）.
27. ↑ .  [2008-04-13]. （原始内容存档于2021-02-05）.
28. ↑ .  [2020-09-11]. （原始内容存档于2021-06-15）.
29. ↑ .  [2008-04-13]. （原始内容存档于2012-03-13）.
30. ↑   (PDF).  [2008-04-13]. （原始内容存档 (PDF)于2021-05-10）.
31. ↑ .  [2011-04-23]. （原始内容存档于2021-01-29）.
32. ↑ .  [2012-03-19]. （原始内容存档于2017-02-14）.
33. ↑ .  [2020-03-31]. （原始内容存档于2021-04-11）.
34. ↑ .  [2020-03-31]. （原始内容存档于2021-02-01）.
35. ↑ .  [2016-08-21]. （原始内容存档于2021-06-26）.
36. ↑ .  [2021-03-30]. （原始内容存档于2021-05-08）.
37. ↑ .  [2014-07-13]. （原始内容存档于2010-02-26）.
38. ↑ .  [2021-03-30]. （原始内容存档于2016-01-23）.
39. ↑ .  [2021-03-30]. （原始内容存档于2020-11-12）.
40. ↑ .  [2021-03-30]. （原始内容存档于2020-11-22）.
41. ↑  openmethods
42. ↑ .  [2014-07-13]. （原始内容存档于2021-04-29）.
43. ↑ .  [2014-07-13]. （原始内容存档于2021-04-11）.
44. ↑ .  [2014-07-13]. （原始内容存档于2013-10-21）.
45. ↑ .  [2021-03-30]. （原始内容存档于2021-04-29）.
46. ↑ .  [2021-03-30]. （原始内容存档于2021-04-30）.
47. ↑ .  [2021-03-30]. （原始内容存档于2021-04-29）.
48. ↑ .  [2021-03-30]. （原始内容存档于2021-04-27）.
49. ↑ .  [2014-07-13]. （原始内容存档于2008-12-11）.

外部链接
----

* Stroustrup, Bjarne; Solodkyy, Yuriy; Pirkelbauer, Peter.  (PDF). ACM 6th International Conference on Generative Programming and Component Engineering. 2007  [2014-07-13]. （原始内容存档 (PDF)于2021-04-29）.
*. docs.racket-lang.org.  [2018-03-12]. （原始内容存档于2021-04-29）.