多分派

多分派或譯多重派發（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