Мультиметод
Мультиме́тод (англ. multimethod) або множи́нна диспетчериза́ція (англ. multiple dispatch) — механізм, який дозволяє вибрати одну з декількох функцій в залежності від динамічних типів або значень аргументів. Є розширенням одиночної диспетчеризації (віртуальних функцій), де вибір методу здійснюється динамічно на основі фактичного типу об'єкта. Множинна диспетчеризація узагальнює динамічну диспетчеризацію для випадків з двома або більшою кількістю об'єктів.
В явному вигляді мультиметоди підтримуються «об'єктною системою типів Common Lisp’а» (CLOS).
Приклад
Для кращого розуміння різниці між мультиметодами і одиночною диспетчеризацією можна продемонструвати наступний приклад. Уявимо собі гру, в якій поряд з різноманітними іншими об'єктами, присутні астероїди та космічні кораблі. Коли два яких-небудь об'єкти зіштовхуються, програма повинна вибрати певний алгоритм дій, в залежності від того що із чим зіштовхнулось.
В мові з підтримкою мультиметодів, такій, як Common Lisp, код виглядав би так:
(defgeneric collide (x y))
(defmethod collide ((x asteroid) (y asteroid))
;;астероїд зіштовхується з астероїдом
)
(defmethod collide ((x asteroid) (y spaceship))
;;астероїд зіштовхується з космічним кораблем
)
(defmethod collide ((x spaceship) (y asteroid))
;;космічний корабель зіштовхується з астероїдом
)
(defmethod collide ((x spaceship) (y spaceship))
;;космічний корабель зіштовхується з космічним кораблем
)
namespace MultipleDispatch
{
enum Outcome
{
Win,
Lose,
Draw,
}
abstract class Item
{
public abstract Outcome Compete(Item item);
public abstract Outcome Evaluate(Paper paper);
public abstract Outcome Evaluate(Scissors scissors);
public abstract Outcome Evaluate(Rock rock);
}
class Paper : Item
{
public override Outcome Compete(Item item) => item.Evaluate(this);
public override Outcome Evaluate(Paper paper) => Outcome.Draw;
public override Outcome Evaluate(Scissors scissors) => Outcome.Win;
public override Outcome Evaluate(Rock rock) => Outcome.Lose;
}
class Scissors : Item
{
public override Outcome Compete(Item item) => item.Evaluate(this);
public override Outcome Evaluate(Paper paper) => Outcome.Lose;
public override Outcome Evaluate(Scissors scissors) => Outcome.Draw;
public override Outcome Evaluate(Rock rock) => Outcome.Win;
}
class Rock : Item
{
public override Outcome Compete(Item item) => item.Evaluate(this);
public override Outcome Evaluate(Paper paper) => Outcome.Win;
public override Outcome Evaluate(Scissors scissors) => Outcome.Lose;
public override Outcome Evaluate(Rock rock) => Outcome.Draw;
}
class Program
{
static Item GetRandomItem()
{
switch (new System.Random().Next(3))
{
case 0: return new Paper();
case 1: return new Scissors();
case 2: return new Rock();
default: throw new System.InvalidOperationException("Wrong item type");
}
}
static void Main(string[] args)
{
Item item1 = GetRandomItem();
Item item2 = GetRandomItem();
System.Console.WriteLine($"{item1.GetType().Name} - {item1.Compete(item2)} - {item2.GetType().Name}");
}
}
}
namespace MultipleDispatchPattern
{
enum Outcome
{
Win,
Lose,
Draw,
}
abstract class Item
{
public Outcome Compete(Item item) => Evaluate(this as dynamic, item as dynamic);
private static Outcome Evaluate(Paper paper1, Paper paper2) => Outcome.Draw;
private static Outcome Evaluate(Paper paper, Scissors scissors) => Outcome.Lose;
private static Outcome Evaluate(Paper paper, Rock rock) => Outcome.Win;
private static Outcome Evaluate(Scissors scissors, Paper paper) => Outcome.Win;
private static Outcome Evaluate(Scissors scissors1, Scissors scissors2) => Outcome.Draw;
private static Outcome Evaluate(Scissors scissors, Rock rock) => Outcome.Lose;
private static Outcome Evaluate(Rock rock, Paper paper) => Outcome.Lose;
private static Outcome Evaluate(Rock rock, Scissors scissors) => Outcome.Win;
private static Outcome Evaluate(Rock rock1, Rock rock2) => Outcome.Draw;
}
class Paper : Item
{
}
class Scissors : Item
{
}
class Rock : Item
{
}
class Program
{
static Item GetRandomItem()
{
switch (new System.Random().Next(3))
{
case 0: return new Paper();
case 1: return new Scissors();
case 2: return new Rock();
default: throw new System.InvalidOperationException("Wrong item type");
}
}
static void Main(string[] args)
{
Item item1 = GetRandomItem();
Item item2 = GetRandomItem();
System.Console.WriteLine($"{item1.GetType().Name} - {item1.Compete(item2)} - {item2.GetType().Name}");
}
}
}
Посилання
- Bjarne Stroustrup; Yuriy Solodkyy; Peter Pirkelbauer (2007). Open Multi-Methods for C++ ACM 6th International Conference on Generative Programming and Component Engineering.