多分派

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

**理解分派**
软件工程师通常把代码写进代码块中，代码块通常称作过程、函数、方法。代码通过被调用来执行，调用时将控制权传入函数中，当函数执行完成后将控制权返回给调用者。
函数名通常用来描述函数的目的。有时会将多个函数起同样的名称。比如同名函数在逻辑上处理相同的任务，但是操作在不同类型的输入值上。在这种情况下，无法仅仅通过函数名，来判断目标代码块。那幺，函数的实际参数的个数和类型，也就被用来判断。
通常，单分派面向对象语言，在调用一个方法时，方法参数中一个参数会被特殊对待，并用来决定哪一个方法（如果有多个同名方法）会被调用。在许多语言中，这个特殊的参数是在语法上指明的，许多编程语言在调用方法时，把特殊参数放在小圆点（.）之前。例如 special.method(other, arguments, here)，这样 lion.sound() 将会发出狮吼，同时 sparrow.sound() 只会吱吱地叫。一般来说，对于面向对象的编程语言，这个小圆点之前的参数（上例中的lion和sparrow）被称为接收者。
相反，在实现了多分派的语言中，被调用的函数，即是那些参数个数一样多，并且类型也匹配的调用。在调用中并没有特殊参数，来决定那个方法被调用。也就是说，所有参数的运行时类型都参与分派。CLOS是早期和著名的多分派语言。

**数据类型**
对于编译时间可以区分数据类型的编程语言，在交替（alternative）函数中进行选择，可以发生在编译时间，创建交替函数用于编译时间选择的活动，通常被叫做函数重载。
在有些编程语言中，这种数据类型的识别，可以被延后至运行时间（Late binding）。交替函数的选择发生在运行时间，并依据动态确定的函数实际参数的类型。以这种方式选择交替实现的函数，通常被称为多方法。

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

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

**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 
#include 
#include 
/* 类 */
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(" = %d\n", collide_with(a, a));
printf(" = %d\n", collide_with(a, s));
printf(" = %d\n", collide_with(s, a));
printf(" = %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 collisionCasesTHING_COUNTTHING_COUNT = {
{&collision_AA, &collision_AS},
{&collision_SA, &collision_SS}
};
void collide(Thing a, Thing b) {
(*collisionCasesab)();
}
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检查可以在一个或两个层次上使用。

**代码示例**

**引用**

**外部链接**
dead-url=no