多重ディスパッチ(Multiple dispatch)とは、
多重定義された関数やメソッド(マルチメソッド)において、どの定義を呼び出すかを複数の引数に基づいて動的に決定する仕組みです。これは、単一ディスパッチのように一つの引数のみで決定するのではなく、複数の引数の型や値によって実行するコードを選択します。
概要
多重定義をサポートする
プログラミング言語では、同じ名前の関数やメソッドが複数存在する場合、どの関数やメソッドを呼び出すかを決定する必要があります。多くのオブジェクト指向
プログラミング言語は、単一ディスパッチを採用しており、メソッド呼び出し時に特定の引数(通常はオブジェクト自身)に基づいて呼び出すメソッドを選択します。
例えば、`object.method(arg1, arg2)` のようなコードでは、`object` の型に基づいて `method` の実装が選択されます。これは、メソッドがオブジェクトに「属する」という考え方に基づいています。
一方、多重ディスパッチでは、全ての引数がメソッド選択の対象となります。どの引数がメソッドを「所有」しているという概念はなく、引数の型や値の組み合わせによって、適切なメソッドが選択されます。二つの引数のみを対象とする多重ディスパッチは、特にダブルディスパッチと呼ばれます。
例
多重ディスパッチと単一ディスパッチの違いを理解するために、宇宙船と小惑星が衝突するゲームの例を考えてみましょう。衝突するオブジェクトの組み合わせによって、異なる処理が必要になります。
Java (単一ディスパッチ)
Java のような単一ディスパッチの言語では、以下のようなコードになるでしょう(ただし、Visitorパターンを利用することも可能です)。
java
class SpaceShip {
void collideWith(Object other) {
if (other instanceof Asteroid) {
// 宇宙船と小惑星の衝突処理
} else if (other instanceof SpaceShip) {
// 宇宙船同士の衝突処理
}
}
}
class Asteroid {
void collideWith(Object other) {
if (other instanceof SpaceShip) {
// 小惑星と宇宙船の衝突処理
} else if (other instanceof Asteroid) {
// 小惑星同士の衝突処理
}
}
}
このコードでは、`collideWith`メソッド内で引数の型をチェックし、処理を分岐する必要があります。
Common Lispのような多重ディスパッチの言語では、以下のようなコードになります。
lisp
(defgeneric collide-with (object1 object2))
(defmethod collide-with ((ship spaceship) (asteroid asteroid))
;; 宇宙船と小惑星の衝突処理
)
(defmethod collide-with ((ship1 spaceship) (ship2 spaceship))
;; 宇宙船同士の衝突処理
)
(defmethod collide-with ((asteroid asteroid) (ship spaceship))
;; 小惑星と宇宙船の衝突処理
)
(defmethod collide-with ((asteroid1 asteroid) (asteroid2 asteroid))
;; 小惑星同士の衝突処理
)
引数の型に応じて、`collide-with`メソッドが自動的に選択され、コードが簡潔になります。メソッドは特定のクラスに「属する」のではなく、引数の型に基づいて選択される「普通の関数呼び出し」として扱われます。これにより、メソッドを呼び出すための特殊な構文は必要ありません。
Pythonは標準では多重ディスパッチをサポートしていませんが、ライブラリを利用することで同様の機能を追加できます。
例えば、`multimethods.py`モジュールを使用すると、以下のように記述できます。
python
from multimethods import multimethod
@multimethod
def collide(ship: SpaceShip, asteroid: Asteroid):
# 宇宙船と小惑星の衝突処理
pass
@multimethod
def collide(ship1: SpaceShip, ship2: SpaceShip):
# 宇宙船同士の衝突処理
pass
この例では、`@multimethod`デコレータを使用して、引数の型に基づいて適切な`collide`関数を選択しています。
多重ディスパッチを標準でサポートする言語の例:
Common Lisp
Clojure
Dylan
Nice
Slate
Cecil
Raku
Julia
拡張機能によって多重ディスパッチをサポートする言語の例:
Scheme (Tiny CLOS)
Python (gnosis.magic.multimethods)
Perl (Class::Multimethods)
Java (Multi
Java)
*
Ruby (The Multiple Dispatch Library, Multimethod Package)
多重ディスパッチは、柔軟で拡張性の高いプログラミングを可能にする強力なツールです。特に、複数の型が関わる処理を記述する際に、コードの可読性と保守性を向上させることができます。