Здравствуйте, remark, Вы писали:
R>Не согласен. Визитор просто позволяет вынести виртуальный метод из иерархии классов в один отдельный класс.
Мы точно под визитором одно и том же понимаем?
R>Нет, его конечно можно назвать мультиметодом для объектов (тип действия, класс),
Мультиметод чистейшей воды, просто реализация несколько неуклюжа.
R> но это называется просто методом. Диспетчерезацию по типу метода всегда выполняет сам программист, когда после точки пишет имя метода.
В классической реализации визитора имя метода Visit всегда одно и то же, а диспетчеризацию выполняет компилятор за счет перегрузки этого метода по типу возвращаемого значения.
... << RSDN@Home 1.2.0 alpha rev. 644 on Windows XP 5.1.2600.131072>>
Здравствуйте, 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, // передаем указатель на обработчик типа object
true));
...
}
[StripEventHandler("del")]
private void DeleteTagLine()
{
...
}
[StripEventHandler("edit")]
private void EditTagLine()
{
...
}
[StripEventHandler("add")]
private void AddTagLine()
{
...
}
}
<items>
<button text="Config.TagLine.Editor.Add" event-id="add" image="new"/>
<button text="Config.TagLine.Editor.Edit" event-id="edit" image="editmsg"/>
<button text="Config.TagLine.Editor.Delete" event-id="del" image="del"/>
</items>
Как видишь, без проблем обошлись без мультиметодов, встроенных в язык.
... << RSDN@Home 1.2.0 alpha rev. 644 on Windows XP 5.1.2600.131072>>
Здравствуйте, VladD2, Вы писали:
VD>В Посетителе диспечерезация двойная (по типу объекта коллекции и по типу объекта которому передается обработка). Это конечто наверно относится к множественной диспечеризации, но это все же частный случай.
Частный случай, но уже множественной диспетчеризации
. Если не изменяет память, тот же Александреску делал Visitor с более чем двойной диспетчеризацией, с помощью TypeList'ов, хотя кто его знает...
... << RSDN@Home 1.1.4 stable rev. 510>>
Здравствуйте, Курилка, Вы писали:
К>Собственно сабж.
К>Много читал про то, что в ряде языков мультиметоды реализуются в самом языке (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"))
Когда делается контрол, к нему приделывается дефолтный метод
рисования, который вызывается, когда специфического метода для
данного стиля не определено. В этом случае отрисовка идёт самым
простым путём — чтоб хоть как-то отрисовать. Например, для кнопки
(defmethod paint (style (control button) ...)
;; рисуем плоскую некрасивую кнопку
...)
или для скроллбара
(defmethod paint (style (control scrollbar) ...)
;; рисуем плоский некрасивый скроллбар
...)
(другой вариант — сделать дефолтный базовый стиль и запихать все подобные
методы в один и тот же файл, это как кому удобнее)
Когда делаются стили, то в них описываются методы отрисовки известных
контролов, например для аквы
(defmethod paint ((style aqua) (conrol button) ...)
;; рисуем водянистую кнопку
...)
(defmethod paint ((style aqua) (conrol scrollbar) ...)
;; рисуем водянистый скроллбар
...)
(defmethod paint ((style aqua) (conrol textbox) ...)
;; рисуем водянистый текстбокс
...)
или для виндоус
(defmethod paint ((style windows) (conrol button) ...)
;; рисуем виндообразную кнопку
...)
(defmethod paint ((style windows) (conrol scrollbar) ...)
;; рисуем виндообразный скроллбар
...)
(defmethod paint ((style windows) (conrol textbox) ...)
;; рисуем виндообразный текстбокс
...)
Когда пользователь делает контрол porn-view, он может
сопроводить его не только дефолтной рисовалкой
(defmethod paint (style (control porn-view) ...)
;; нарисовать по-простому
...)
но и набором рисовалок для ряда стилей, например
(defmethod paint ((style aqua) (control porn-view) ...)
;; нарисовать porn-view по-водянистому
...)
(defmethod paint ((style windows) (control porn-view) ...)
;; нарисовать porn-view по-виндовски
...)
(defmethod paint ((style motif) (control porn-view) ...)
;; нарисовать porn-view на мотив мотифа
...)
в свою очередь, если какой-либо ещё дядя вася использует
основную библиотеку, контрол porn-view и ещё одну либу с набором
стилей, и ему не нравится, что в стиле super-puper porn-view
рисуется по-дефолтному, он может дописать
(defmethod paint ((style super-puper) (control porn-view) ...)
;; нарисовать porn-view в стиле супер-пупер
...)
В общем, экстенсибилити не знает границ
В языках типа C# можно ещё извращаться с Reflection, всякие
Type.InvokeMember, но гораздо приятнее, когда есть поддержка языка.
Здравствуйте, 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);
Вот это мультиметод. Согласен. Существительных два. + есть ещё и действие — собственно мультиметодв.
А в первом случае. Есть только одно существительное и действие.
Сравним первый пример с таким:
IShape s = new Circle();
s->DrawShape();
Чем отличается? Кроме организации кода — ничем.