Здравствуйте, VladD2, Вы писали:
VD>Так как 99% языков без них обходятся, то и говорить не о чем. К тому же как я понимаю, фишки языка вроде сопоставления с образцом могут очень достойно заменять мультиметоды. Вот простенький пример на Нэмерле:
Нюанс! Полиморфная функция может быть определена вся в одном месте, либо размазана по системе.
Сопоставление с образцом — это первый случай, а виртуальные функции — второй.
Естественно, тут у обоих подходов есть и сильные, и слабые стороны.
Особенность мультиметодов — в том, что, если не определены все возможные комбинации типов, то возникает неоднозначность, по какой ветке пойти в случае неточного совпадения.
Эту неоднозначность разрешают разными способами.
Самые распространённые
— порядок, определённый пользователем (таблица сопоставления с образцами, собранная вручную)
— лексикографический порядок (двойная диспетчеризация; автоматически заполненная таблица)
На ёлку влезть и рыбку съесть — не получается. Либо мы вводим устраивающий нас порядок и платим за это плохой масштабируемостью и сопровождаемостью (попробуйте отладить таблицу сопоставления, если в иерархиях по 50 классов). Либо подстраиваемся под жёстко прибитый порядок, получая внятную логику и возможность масштабирования.
А фишки языка... они большого значения не имеют. Проблема-то именно архитектурная.
Даже в С++ можем нагородить if(dynamic_cast<X*>(a) && dynamic_cast<Y*>(b))... или что-нибудь подобное. Ну там макросами оформить. В конце концов, MFC строит таблицы виндовских сообщений (кстати, тоже вариация на тему мультиметодов) макросами, и все счастливы.
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж. К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
они позволяют, например, избавляться от уродливых конструкций вида
if (x is TypeA)
{
....
}
else if (x is TypeB)
{
....
}
....
визиторы, кстати, это один из частных случаев мультиметодов — по своей сути эмуляция двойной диспетчеризации
Здравствуйте, Курилка, Вы писали:
К>А теперь к моему вопросу — насколько это востребованно?
Все зависит от задачи.
Например, если задача "распознать" дерево, то можно:
1. Восспользоваться ООП и паттерном Посетитель.
2. Воспользоваться сопоставлением с образцом.
3. Воспользоваться мултиметодами.
Думаю, что вариант 2 и 3 проще в реализауии, более интуитивны (не нужно разглядывать паттерн в его ерализации) и компактнее.
Одако и с Посетителем можно с успехом жить.
Использвать же "if (x is Y)" в ОО-языках крайне не удобно.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
> C>Ага. Классическая двойная диспетчеризация — по типу аргумента (коду > C>сообщения) и по указателю 'this'. > > Хм. Но сообщение является целочисленной константой. То есть второй параметр не полиморфен. Так что это обычный вызов.
Это всё-таки полиморфный вызов с вариантным аргументом. Ну а то, что это всё реализовано на единственной точке входа и целочисленных параметрах — ну так винапи это же, считай, ассемблер.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, remark, Вы писали:
R>>Не согласен. Визитор просто позволяет вынести виртуальный метод из иерархии классов в один отдельный класс.
AVK> Мы точно под визитором одно и том же понимаем?
GOF?
R>>Нет, его конечно можно назвать мультиметодом для объектов (тип действия, класс),
AVK>Мультиметод чистейшей воды, просто реализация несколько неуклюжа.
Наоборот. Реализация похожа на мультиметод, а по сути — виртуальная функция чистейшей воды. Просто вынесенная из самого класса. Но сути это не меняет.
R>> но это называется просто методом. Диспетчерезацию по типу метода всегда выполняет сам программист, когда после точки пишет имя метода.
AVK>В классической реализации визитора имя метода Visit всегда одно и то же, а диспетчеризацию выполняет компилятор за счет перегрузки этого метода по типу возвращаемого значения.
Сейчас попробую объяснить, что я имею в виду.
Как обычно используется визитор:
IShape s = new Circle();
IVisitor v = new DrawShapeVisitor();
s->visit(v);
Что мы тут имеем? "Существительное" — фигура (IShape). "Сказуемое" — нарисовать (DrawShapeVisitor). Т.е. существует всего одно существительное, а не два (и не три). Для мультиметода надо хотя бы два существительных! Например:
IShape s1 = new Circle();
IShape s2 = new Square();
DrawInterseptionMultimethod(sq, s2);
Вот это мультиметод. Согласен. Существительных два. + есть ещё и действие — собственно мультиметодв.
А в первом случае. Есть только одно существительное и действие.
Сравним первый пример с таким:
Здравствуйте, Курилка, Вы писали:
К>Вопрос не в том, есть ли применение вообще, а скорее насколько такие задачи часто встречаются
Имхо всё зависит от конкретного проекта — у меня вот ни разу не было необходимости. В общем, если её нет, то можно сэмулировать средствами языка (как правило) или реализовать по другому, если есть — нехай будет
С другой стороны, возможно, я просто не умею её готовить и поэтому не использую, хотя мог бы.
Представь себе, что у тебя есть набор примитивных объектов, над которыми надо делать, например, операцию сложения. При этом объекты разного типа можно складывать, но правила сложения разные. Например, есть у тебя .NET BCL и в ней есть int, double, DateTime, TimeSpan, которые все между собой замечательно складываются (int + double => double, int + DateTime => DateTime, double + TimeSpan => TimeSpan). Писать такой метод сложения без мультиметодов напряжно — приходится их "реализовывать" через reflection и хеш-таблицы.
Привёл такой пример потому, что в форуме dotnet он пробегал как часть реального проекта.
В общем-то, всему найдётся реальное применение, просто иногда с ходу тяжело его назвать, если сам не сталкивался.
Здравствуйте, Курилка, Вы писали:
Д>>визиторы, кстати, это один из частных случаев мультиметодов — по своей сути эмуляция двойной диспетчеризации
К>Не спорю
Не согласен. Визитор просто позволяет вынести виртуальный метод из иерархии классов в один отдельный класс.
Нет, его конечно можно назвать мультиметодом для объектов (тип действия, класс), но это называется просто методом. Диспетчерезацию по типу метода всегда выполняет сам программист, когда после точки пишет имя метода. Собственно глупо пытаться выполнить какое-то абстрактное действие, не указав, какое конкретно действие хочешь выполнить.
Собственно сабж.
Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж. К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Если для вас задача "что будет если всепробивающее ядро попадет в непрошибаемый столб" жизненно важна, но мультиметоды помогут. Вообще наверное пролезно в играх — там дамажи всякие по юнитам разным — можно сделать лучше чем у близзарда в варкрафте.
У меня был лично еще такой пример — делал очередь операций (Undo/Redo), так вот — некоторые операции — например перемещение объекта, было бе не плохо склеить в одну (потеряв информацию промежуточном состоянии за ненадобностью) и сэкономить памят и все такое. Жутко интерсено узнавать можно ли склеить 2 операции и что получиться в результате. классов операций было около 50 так что скучно все без ММ
Здравствуйте, Oyster, Вы писали:
O>Здравствуйте, Курилка, Вы писали:
O>Представь себе, что у тебя есть набор примитивных объектов, над которыми надо делать, например, операцию сложения. При этом объекты разного типа можно складывать, но правила сложения разные. Например, есть у тебя .NET BCL и в ней есть int, double, DateTime, TimeSpan, которые все между собой замечательно складываются (int + double => double, int + DateTime => DateTime, double + TimeSpan => TimeSpan). Писать такой метод сложения без мультиметодов напряжно — приходится их "реализовывать" через reflection и хеш-таблицы.
O>Привёл такой пример потому, что в форуме dotnet он пробегал как часть реального проекта.
O>В общем-то, всему найдётся реальное применение, просто иногда с ходу тяжело его назвать, если сам не сталкивался.
Вопрос не в том, есть ли применение вообще, а скорее насколько такие задачи часто встречаются
Здравствуйте, Дарней, Вы писали:
Д>они позволяют, например, избавляться от уродливых конструкций вида Д>
Д>if (x is TypeA)
Д>{
Д>....
Д>}
Д>else if (x is TypeB)
Д>{
Д>....
Д>}
Д>....
Д>
А теперь к моему вопросу — насколько это востребованно?
Д>визиторы, кстати, это один из частных случаев мультиметодов — по своей сути эмуляция двойной диспетчеризации
Здравствуйте, Курилка, Вы писали:
К>А теперь к моему вопросу — насколько это востребованно?
подумал я тут над этим вопрос, и пришел к выводу — мультиметоды у меня обычно востребованы, когда нужно строить сложные DOM'ы и операции над их элементами. Сюда можно также включить деревья контролов в гуе.
Помимо этого, мультиметоды редко когда нужны.
Здравствуйте, Курилка, Вы писали:
К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Так как 99% языков без них обходятся, то и говорить не о чем. К тому же как я понимаю, фишки языка вроде сопоставления с образцом могут очень достойно заменять мультиметоды. Вот простенький пример на Нэмерле:
using System.Console;
class A { }
[Record]
class B : A { public Y : string; }
[Record]
class C : A { public Z : int; }
def Func(a1 : A, a2 : A) : void
{
match ((a1, a2))
{ | (_ is B, _ is C) => WriteLine(":)");
| (_ is C, _ is B) => WriteLine(":|");
| _ => WriteLine(":(");
}
}
Func(B("Test 1"), C(2));
Func(C(2), B("Test 2"));
Func(C(2), A());
Этот код выводит:
:)
:|
:(
что аналогично поведению мультиметодов.
Или вариация с использованием значений:
using System.Console;
class A { }
[Record]
class B : A { public Y : string; }
[Record]
class C : A { public Z : int; }
// Здесь используется неявное сопоставление с обрзцом.
def Func(a1 : A, a2 : A) : void
{ | (b is B, c is C) => WriteLine($"a1.Y='$(b.Y)' a2.Z=$(c.Z)");
| (c is C, b is B) => WriteLine($"a1.Z=$(c.Z) a2.Y='$(b.Y)'");
| _ => WriteLine(":(");
}
Func(B("Test 1"), C(2));
Func(C(2), B("Test 2"));
Func(C(2), A());
выводит:
a1.Y='Test 1' a2.Z=2
a1.Z=2 a2.Y='Test 2'
:(
А вот что получается если декомпилировать код в C#:
private static void _N_Func1893(A a1, A a2)
{
Tuple<A, A> tuple1 = new Tuple<A, A>(a1, a2);
if (!(tuple1.field0 is B))
{
if (!(tuple1.field0 is C))
{
goto Label_0093;
}
C c1 = (C) tuple1.field0;
if (!(tuple1.field1 is B))
{
goto Label_0093;
}
B b1 = (B) tuple1.field1;
StringBuilder builder2 = new StringBuilder();
builder2.Append("a1.Z=");
builder2.Append(c1.Z);
builder2.Append(" a2.Y='");
builder2.Append(b1.Y);
builder2.Append("'");
Console.WriteLine(builder2.ToString());
return;
}
B b2 = (B) tuple1.field0;
if (tuple1.field1 is C)
{
C c2 = (C) tuple1.field1;
StringBuilder builder1 = new StringBuilder();
builder1.Append("a1.Y='");
builder1.Append(b2.Y);
builder1.Append("' a2.Z=");
builder1.Append(c2.Z);
Console.WriteLine(builder1.ToString());
return;
}
Label_0093:
Console.WriteLine(":(");
}
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Ну вот у меня из недавнего. Есть некий движок-интерпретатор, и есть некое множество арифметических операций над объектами числами. Все бы ничего, да вот только типов этих чисел очень много:
— обычные целые
— обычные с плавающей точкой
— "длинные" целые (не int64, а именно бесконечной длины)
— дроби (т.е. число, представленное как пара: числитель/знаменатель)
Соответственно, при арифметических действиях алгоритм и сам результат зависит от взаимных типов операндов. Естественно здесь применить двойную диспечеризацию, но на мультиметодах запись была бы эстетичнее, что ли...
Здравствуйте, Кодт, Вы писали:
К>Естественно, тут у обоих подходов есть и сильные, и слабые стороны.
+1
К>Особенность мультиметодов — в том, что, если не определены все возможные комбинации типов, то возникает неоднозначность, по какой ветке пойти в случае неточного совпадения. К>Эту неоднозначность разрешают разными способами. К>Самые распространённые К>- порядок, определённый пользователем (таблица сопоставления с образцами, собранная вручную) К>- лексикографический порядок (двойная диспетчеризация; автоматически заполненная таблица)
Кстати, твои слова навели на мысль, что мультиметоды вообще мало применимы в компонентных стистемах, так как они могут содержаться в разных модуля, в том числе и подгружаемых динамически. Это приведет или к неверному поведению, или (при динамической реструктуризации) к нехилым проблемам с логическими ошибками. Так что я уже и не хочу связываться с мультиметодами. Все же то, что код сопоставления в одном месте дает детерминированность. Гибкость мультиметодов может выйти боком.
К>А фишки языка... они большого значения не имеют. Проблема-то именно архитектурная.
Дык если есть сопоставление с образцом, то мы имеем средство для решения большинства задачь без сильных побочных эффектов. А что еще надо?
К>Даже в С++ можем нагородить if(dynamic_cast<X*>(a) && dynamic_cast<Y*>(b))... или что-нибудь подобное. Ну там макросами оформить. В конце концов, MFC строит таблицы виндовских сообщений (кстати, тоже вариация на тему мультиметодов) макросами, и все счастливы.
Счастливы только маньяки или психи. Тот кто видел полноценную компонентную систему построения GUI вряд ли испытает щастье от левой эмуляции на макросах.
Кстати, о чем в MFC ты говоришь как о мультиметодах? О картах сообщений?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote: > Счастливы только маньяки или психи. Тот кто видел полноценную > компонентную систему построения GUI вряд ли испытает щастье от левой > эмуляции на макросах. > Кстати, о чем в MFC ты говоришь как о мультиметодах? О картах сообщений?
Ага. Классическая двойная диспетчеризация — по типу аргумента (коду
сообщения) и по указателю 'this'.
VladD2 wrote: > C>Ага. Классическая двойная диспетчеризация — по типу аргумента (коду > C>сообщения) и по указателю 'this'. > Хм. Но сообщение является целочисленной константой. То есть второй > параметр не полиморфен. Так что это обычный вызов.
Ну представь, что вместо WM_SETTEXT у тебя объект SetTextMessageData,
унаследованый от MessageData.
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж. К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Я думаю так: сейчас мультиметоды не очень нужны, но только потомучто они промушленными языками не поддержтваются, а как следствие люди не только мало чего про них знают, но и просто не умеют их выделять. Т.е. смотрят на какой-то код, но не понимают, что фактически имеют дело с мультиметодами.
Я, когда первый раз читал классический пример про пересечение фигур, думал "о чём вообще ребята говорят, я про это не знаю и мне без этого нормально живётся"
Примеров много. Фактически, когда у тебя есть не N действий, а N*M действий в системе — это мультиметод.
Далеко ходить не надо — N типов GUI окон * M типов событий — получается N*M действий. Да это мультиметод, несмотря на то, что мало кто об этом задумывается.
С поддержкой в языке многие вещи стали бы проще (кстати, есть предложение по включению в c++ мультиметодов). Сейчас с ними такая же ситуация, как с динамическим полиморфизмом во время С, когда в языке не было поддержки. Т.е. — реализовывали вручную + многие даже не знали как это называется, просто приходили к такому решению.
Я об этом задумался, когда сам споткнулся на плёвой ситуации — попытался реализовать publisher/subscriber на с++ — не получается. Думаю, чо за фигня — не получается реализовать такую простую вещь. Нет, можно конечно, но для этого надо в интерфейсе ISubscriber сделать по методу для каждого типа события:
То, что в Action1 надо реализовывать dispatch, это меня ещё устраивало — это можно сделать на шаблонах, но то что в ISubscriber надо прописать по методы на каждое событие, меня не устраивало. Т.к. система задумывалась как расширяемая.
И сейчас в промышленных языках (С++/С#/Java) эту задачу просто не решить (я имею в виду, что решить, но извращаться нужно).
Если бы была поддержка со стороны языка — это бы решалось элементарно — так же как и динамический полиморфизм.
Кстати, если вернуться к MFC и обработке событий окнами. Сейчас мы имеем очень кривую систему — все эти макросы, switch, LPARAM, WPARAM — бред. Если бы в с++ была поддержка мультиметодов, то возможно мы писали бы просто:
Т.е. — не надо карт событий, не надо всех этих reinterpret_cast, динамического создания/удаления параметров события. Всё было бы красиво, типобезопасно и автоматически.
Здравствуйте, remark, Вы писали:
R>Не согласен. Визитор просто позволяет вынести виртуальный метод из иерархии классов в один отдельный класс.
Мы точно под визитором одно и том же понимаем?
R>Нет, его конечно можно назвать мультиметодом для объектов (тип действия, класс),
Мультиметод чистейшей воды, просто реализация несколько неуклюжа.
R> но это называется просто методом. Диспетчерезацию по типу метода всегда выполняет сам программист, когда после точки пишет имя метода.
В классической реализации визитора имя метода Visit всегда одно и то же, а диспетчеризацию выполняет компилятор за счет перегрузки этого метода по типу возвращаемого значения.
... << RSDN@Home 1.2.0 alpha rev. 644 on Windows XP 5.1.2600.131072>>
Здравствуйте, remark, Вы писали:
R>Не согласен. Визитор просто позволяет вынести виртуальный метод из иерархии классов в один отдельный класс.
Но это всё равно, что сказать: "пиво просто позволяет чаще посещать туалет", умолчав о вкусовых качествах этого напитка.
Одна из основных идея visitor'а — именно полиморфизм со множественной диспетчеризацией. Можете вот книжку Александреску почитать про это.
Здравствуйте, remark, Вы писали:
R>И сейчас в промышленных языках (С++/С#/Java) эту задачу просто не решить (я имею в виду, что решить, но извращаться нужно).
На C# эта задача просто решается без каких либо извращений при помощи атрибутов и делегатов. Вот реальный код, использующий это:
internal class TagLineListForm : Form
{
public TagLineListForm()
{
...
_toolStrip.Items.AddRange(
StripSerializer.LoadFromXmlWithDispatch(
typeof (TagLineListForm).Assembly.GetManifestResourceStream(
"Rsdn.Janus.Features.MessageEditor.TagLine.EditorToolbar.xml"),
this, // передаем указатель на обработчик типа objecttrue));
...
}
[StripEventHandler("del")]
private void DeleteTagLine()
{
...
}
[StripEventHandler("edit")]
private void EditTagLine()
{
...
}
[StripEventHandler("add")]
private void AddTagLine()
{
...
}
}
Здравствуйте, AndrewVK, Вы писали:
AVK>В классической реализации визитора имя метода Visit всегда одно и то же, а диспетчеризацию выполняет компилятор за счет перегрузки этого метода по типу возвращаемого значения.
Классических вариантов два. Один с перегрузкой, другой нет. Я видил даже где-то спор как лучше методы называть. Одни орали, что перегрузка удобнее, а другие, что мол так понятнее.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladGalkin, Вы писали:
VG>Одна из основных идея visitor'а — именно полиморфизм со множественной диспетчеризацией. Можете вот книжку Александреску почитать про это.
В Посетителе диспечерезация двойная (по типу объекта коллекции и по типу объекта которому передается обработка). Это конечто наверно относится к множественной диспечеризации, но это все же частный случай.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>В Посетителе диспечерезация двойная (по типу объекта коллекции и по типу объекта которому передается обработка). Это конечто наверно относится к множественной диспечеризации, но это все же частный случай.
Частный случай, но уже множественной диспетчеризации . Если не изменяет память, тот же Александреску делал Visitor с более чем двойной диспетчеризацией, с помощью TypeList'ов, хотя кто его знает...
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж. К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Простой, может быть, конечно, несколько дурацкий пример — GUI'шная либа со стилями. Подобная
задача обычно решается при помощи visitor / acyclic visitor, и выходит достаточно неудобоваримо.
Есть набор контролов (или виджетов, кому как больше нравится) — кнопка, поле ввода, скроллбар и пр.,
а также набор стилей — виндоус, аква, мотиф и пр. Стиль имеет вид вроде
public class Style: BaseStyle
{
...
public override void Paint(Button button, ...)
{
...
}
public override void Paint(TextBox textBox, ...)
{
...
}
public override void Paint(ScrollBar scrollBar, ...)
{
...
}
...
}
соответственно в каждом контроле есть метод Paint, например
public class Button: Control
{
...
public override void Paint(Style style, ...)
{
style.Paint(this, ...)
}
...
}
Это, собственно, визитор — вызов style.Paint выбирает нужный метод
через оверлоадинг в период компиляции. В чём неудобство?
Допустим, пользователю библиотеки надо добавить ещё какой-то контрол,
например, PornView. Более того, допустим, у пользователя библиотеки есть чёткое
представление, как этот контрол должен выглядеть в стиле виндоус, аква
и пр. Что придётся делать пользователю? Извращаться — ведь интерфейс
стиля не расширишь, придётся проверять тип стиля в Paint, затем
для каждого стиля рисовать по-своему, или ещё что похуже. Нету в
BaseVisitor метода Paint(PornView pornView, ...), и ничего тут неэ
попишешь. Библиотеку менять нельзя. В общем, фигня. Подобные вещи
можно до какой-то степени облегчить при помощи Acyclic Visitor, но
это тоже не фунт изюму, и потом ещё в этом самому разбираться
придётся...
Рассмотрим, как такое решается в языке с поддержкой мультиметодов —
например, Common Lisp. (троеточия здесь, как и в случае C#, не являются
синтаксической конструкцией, а обозначают опущенные параметры)
Есть generic function, вообще не привязанный ни к какому классу:
(defgeneric paint (style control ...)
(:documentation "Paint the CONTROL using STYLE"))
Когда делается контрол, к нему приделывается дефолтный метод
рисования, который вызывается, когда специфического метода для
данного стиля не определено. В этом случае отрисовка идёт самым
простым путём — чтоб хоть как-то отрисовать. Например, для кнопки
в свою очередь, если какой-либо ещё дядя вася использует
основную библиотеку, контрол porn-view и ещё одну либу с набором
стилей, и ему не нравится, что в стиле super-puper porn-view
рисуется по-дефолтному, он может дописать
(defmethod paint ((style super-puper) (control porn-view) ...)
;; нарисовать porn-view в стиле супер-пупер
...)
В общем, экстенсибилити не знает границ
В языках типа C# можно ещё извращаться с Reflection, всякие
Type.InvokeMember, но гораздо приятнее, когда есть поддержка языка.
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж. К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (CLOS, Cecil, Dylan, Self), только разбираемые примеры выглядят несколько искуственными с той точки зрения, что подобные задачи (когда результат определяется как некое "пересечение" 2-х и более типов объектов) встречаются, на мой взгляд, относительно редко. Поэтому есть мысль, что, несмотря на крутость "встроенности" мультиметодов, в реальной жизни они не так часто дают выгоду, т.к. являются интересной фенечкой лишь иногда позволяющей изящно решить задачу.
Мультиметоды применяются во всех случаях, когда ты используешь double dispatch. Необходимость в нем возникает, например, когда ты хочешь по человечески реализовать конечный автомат в духе ООП. И это не единственный пример — их дофига.
Double dispatch уродует код, радикально ухудшая читабельность. Плюс, можно свернуть голову придумывая разумное обоснование этого, по сути — хака, в терминах предметной области.
Мультиметоды решают эту проблему, устраняя вредный паттерн проектирования.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, remark, Вы писали:
R>>И сейчас в промышленных языках (С++/С#/Java) эту задачу просто не решить (я имею в виду, что решить, но извращаться нужно).
AVK>На C# эта задача просто решается без каких либо извращений при помощи атрибутов и делегатов. Вот реальный код, использующий это:
[skip]
AVK>Как видишь, без проблем обошлись без мультиметодов, встроенных в язык.
Ну возможно я немного загнул по поводу других языков, кроме с++. У C# тоже (как и у С++) есть много очень сильных сторон (в плане реализации интересных/полезных моментов). Это я полностью согласен.
А, кстати, в приведённом тобой примере можно с событием типобезопасно передать и произвольный объект с параметрами?
Здравствуйте, remark, Вы писали:
R>Наоборот. Реализация похожа на мультиметод, а по сути — виртуальная функция чистейшей воды. Просто вынесенная из самого класса. Но сути это не меняет.
у GOF прямо написано, что визитор — это эмуляция множественной диспетчеризации, которая в других языках поддерживается напрямую. И в качестве примера приводится CLOS. О чем тут спорить то?