多分派

多分派或譯多重派發基於它的實際引數的型別，或在更一般的情況下於此之外的其他特性，來動態分派。這是對單分派多型的推廣， 那裡的函式或方法呼叫，基於在其上呼叫方法的物件的派生型別，而動態分派。多分派使用一個或多個實際引數的組合特徵，路由動態分派至實現函式或方法。理解分派.軟體工程師通常把程式碼寫進程式碼塊中，程式碼塊通常稱作過程、函式、方法。程式碼透過被呼叫來執行，呼叫時將控制權傳入函式中，當函式執行完成後將控制權返回給呼叫者。函式名通常用來描述函式的目的。有時會將多個函式起同樣的名稱。比如同名函式在邏輯上處理相同的任務，但是操作在不同型別的輸入值上。在這種情況下，無法僅僅透過函式名，來判斷目的碼塊。那麼，函式的實際引數的個數和型別，也就被用來判斷。通常，單分派面嚮物件語言，在呼叫一個方法時，方法引數中一個引數會被特殊對待，並用來決定哪一個方法被稱為接收者。相反，在實現了多分派的語言中，被呼叫的函式，即是那些引數個數一樣多，並且型別也匹配的呼叫。在呼叫中並沒有特殊引數，來決定那個方法被呼叫。也就是說，所有引數的執行時型別都參與分派。CLOS是早期和著名的多分派語言。資料型別.對於編譯時間可以區分資料型別的程式語言，在函式中進行選擇，可以發生在編譯時間，建立交替函式用於編譯時間選擇的活動，通常被叫做函式過載。在有些程式語言中，這種資料型別的識別，可以被延後至執行時間。交替函式的選擇發生在執行時間，並依據動態確定的函式實際引數的型別。以這種方式選擇交替實現的函式，通常被稱為多方法。例子.可以透過例子更加清晰的區分多分派和單一分派。假想一個遊戲，它有兩種物體：飛船和小行星。當兩個物體要相撞的時候，程式需要依據什麼物體要相撞而做不同的事情。具有內建多分派的語言.Common Lisp.在具有多分派的Common Lisp語言中，可以在Common Lisp物件系統中如下這樣實現：;; deal with asteroid hitting asteroid;; deal with asteroid hitting spaceship;; deal with spaceship hitting asteroid;; deal with spaceship hitting spaceship並且對其他方法也是類似的。沒有使用顯式測試和“動態轉換”。由於多分派的存在，方法要定義在類中幷包含在物件中的傳統想法，變得不再吸引人了，上述每個codice_7方法，都附屬於兩個不同的類而非一個類。因此方法呼叫的特殊語法，一般會消失，從而方法呼叫看起來完全就像正常的函式呼叫，並且方法被組織入泛化函式而非類中。Julia.Julia有內建的多分派，並且它是語言設計的中心。Julia版本的例子如下：collide_with(x::Asteroid, y::Asteroid) = ... # deal with asteroid hitting asteroidcollide_with(x::Asteroid, y::Spaceship) = ... # deal with asteroid hitting spaceshipcollide_with(x::Spaceship, y::Asteroid) = ... # deal with spaceship hitting asteroidcollide_with(x::Spaceship, y::Spaceship) = ... # deal with spaceship hitting spaceshipC#.C#在版本4中介入的codice_9表示式。像很多其他靜態型別的語言語言一樣，C#還支援靜態方法過載，Microsoft預期開發者在多數場景下會選用靜態型別超過動態型別。codice_8關鍵字支援COM物件和動態型別的.NET語言的互操作。class Programstatic 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 Colliderpublic static string Collide(SpaceObject x, SpaceObject y) =&gt;((x.Size &gt; 100) &amp;&amp; (y.Size &gt; 100)) ?"Big boom!" : CollideWith(x as dynamic, y as dynamic);private static string CollideWith(Asteroid x, Asteroid y) =&gt; "a/a";private static string CollideWith(Asteroid x, Spaceship y) =&gt; "a/s";private static string CollideWith(Spaceship x, Asteroid y) =&gt; "s/a";private static string CollideWith(Spaceship x, Spaceship y) =&gt; "s/s";abstract class SpaceObjectpublic SpaceObject(int size) =&gt; Size = size;class Asteroid : SpaceObjectclass Spaceship : SpaceObject輸出：big-booma/ss/sGroovy.Groovy是通用的Java相容/互用的JVM語言，它對立於Java，使用後期繫結/多分派。/*Groovy implementation of C# example aboveLate 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 &gt; 100 &amp;&amp; y.size &gt; 100) ? "big-boom" : collideWith(x, y) // Dynamic dispatch to collideWith methodclass SpaceObject {int size用多分派庫擴充套件的語言.在不於語言定義或語法層次支援多分派的語言中，可能經常使用庫擴充套件來增加多分派。JavaScript.JavaScript和TypeScript不在語言語法層次上支援多方法，但可以透過庫來增加多分派。例如，使用multimethod包，它提供了多分派、泛化函式的實現。JavaScript的動態型別版本：import { multi, method } from '@arrows/multimethod'const collideWith = multi(method([Asteroid, Asteroid], (x, y) =&gt; {// deal with asteroid hitting asteroidmethod([Asteroid, Spaceship], (x, y) =&gt; {// deal with asteroid hitting spaceshipmethod([Spaceship, Asteroid], (x, y) =&gt; {// deal with spaceship hitting asteroidmethod([Spaceship, Spaceship], (x, y) =&gt; {// deal with spaceship hitting spaceship)TypeScript有對應的靜態型別版本。Python.可以使用庫擴充套件來向Python增加多分派。例如，最早的模組multimethods.py，它為Python提供了CLOS風格的多方法而不用變更語言的底層語法或關鍵字。在功能上，這非常類似於CLOS例子，但是語法是常規Python的。Guido van Rossum使用Python 2.4介入的修飾器，出品了多方法的具有簡化了的語法的一個簡單實現，他為此定義了codice_11修飾器。multipledispatch採用的形式與之一致。模組multimethod，採用了修飾器和Python 3.5介入的型別提示實現多方法：from multimethod import multimethodclass Asteroid(): passclass Spaceship(): pass@multimethoddef collide_with(x: Asteroid, y: Asteroid):deal with asteroid hitting asteroidprint("asteroid hitting asteroid")@multimethoddef collide_with(x: Asteroid, y: Spaceship):deal with asteroid hitting spaceshipprint("asteroid hitting spaceship")@multimethoddef collide_with(x: Spaceship, y: Asteroid):deal with spaceship hitting asteroidprint("spaceship hitting asteroid")@multimethoddef collide_with(x: Spaceship, y: Spaceship):deal with spaceship hitting spaceshipprint("spaceship hitting spaceship")»&gt; a = Asteroid()»&gt; b = Spaceship()»&gt; collide_with(a, b)asteroid hitting spaceship此外，還有PEAK-Rules包提供語法類似上述例子的多分派，它後來被替代為PyProtocols。Reg庫也支援多分派和謂詞分派。C.C語言使用C Object System庫，可以支援類似於CLOS的動態分派。它是完全可擴充套件的並且方法不需要任何的手工處理。動態訊息透過COS分派器來分派，它比Objective-C更快。下面是使用COS的例子：/* 類 */defclass (Asteroid)/* 資料成員 */endclassdefclass (Spaceship)/* 資料成員 */endclass/* 泛化函式 */defgeneric (_Bool, collide_with, _1, _2);/* 多方法 */defmethod (_Bool, collide_with, Asteroid, Asteroid)/* deal with asteroid hitting asteroid */endmethoddefmethod (_Bool, collide_with, Asteroid, Spaceship)/* deal with asteroid hitting spaceship */endmethoddefmethod (_Bool, collide_with, Spaceship, Asteroid)/* deal with spaceship hitting asteroid */endmethoddefmethod (_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);程式語言支援.主範型.支援通用的多方法.透過擴充套件.模擬多分派.C.C語言沒有動態分派，也可以不使用C Object System庫，而以某種形式手工實現。動態分派經常使用codice_12來標識一個物件的子型別，然後可透過在函式指標中查詢這個值來完成。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] = {{&amp;collision_AA, &amp;collision_AS},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.執行時間codice_13檢查可以在一個或兩個層次上使用。程式碼示例.