Pull to refresh

Comments 17

верно подмечено! т.к. сам visitor относится к семейству шаблонов динамической диспетчеризации так же, как и double dispatch, то хотелось «разбавить» статью. visitor подошел бы тоже
А проблема N1 не слишком надуманна? В первом примере кода всё красиво. Выбирается метод с типом Node. Уже в методе можно разрулить какой именно тип в иерархии. Уже в самой Node потом написать внутренний virtual Cleanup(), чтобы каждый сам себя чистил как нужно.
ok. как быть, если код сторонний?

На самом деле неудобства наступают тогда, когда у вас объекты-данные, особенно если внешние или сгенерированные — добавление объекта вызывает изменения в нескольких местах. Когда количество таких объектов переваливает за первый десяток ( геометрия, например, или любая messaging-based система) — все это начинает вызывать ощутимый дискомфорт даже при чтении, не говоря уже про сопровождение.


Мы тоже используем подобный паттерн для обработки "сообщений" к актерам в Orleans например. И тоже перед этим прошли через стадии var x = input as Foo, и enum внутри объекта.


Я бы сказал что тут пример немного неудачный — паттерн хорошо работает когда у вас event-sourcing система, которая отправляет большое количество разных типов сообщений и бывает нужно получать сообщения не своего базового типа или даже просто иметь возможность очень быстро прикрутить новую обработку без изменения кода в 10 разных местах. Вот тогда этот паттерн очень помогает — просто заводите тип и пишете новый обработчик типа, остальное уже трогать не надо. А еще можно вкрутить централизованный логгинг, пре и пост-хуки и все это буквально в 1-2 линии кода
Например


public async Task ReceiveMsg(MessageEnvelopeBase item)
{
  await validate(item as dynamic);

  await preProcessing(item as dynamic);
  await handle(item as dynamic);
  await postProcessing(item as dynamic);

  await logging(item as dynamic);
}
// тут определяем осмысленные методы для обработки базового класса MessageEnvelopeBase
// А потом по необходимости пишем обработчики валидаторы и хуки для конкретных сообщений, 
// уже больше не трогая центральную точку входа ReceiveMsg(MessageEnvelopeBase item)

Не то что бы это какой-то rocket-science но код выглядит намного элегантнее. Да и разделение логики и данных хорошее — легко тестировать при помощи разных генераторов данных...

Почему не использовать Reflection?

class Sanitize
{
    ...

    public void Cleanup(object x)
    {
        GetType().GetMethod(nameof(Cleanup), new[] { x.GetType() })?.Invoke(this, x);
    }
}

вопрос же не в вызове гипотетического метода у объекта.
нужно: используя ссылку базового типа — выбрать правильную перегрузку.

reflection — не то что план B, а самый H :)
UFO just landed and posted this here
>просто if заменили на switch

спасибо кэп) только вот не надо путать возможности системы типов и ООП. есть системы, где этого нет.

>В таких случаях я всегда применяю Visitor
круто! принцип timtowtdi должен быть всегда, но по теме?
причинно-следственную связь между параграфами держать не обязательно?
т.е. надо писать статью в виде твита: не используешь visitor, тогда мы идем к вам, да? изобиловать выражениями вида «ну вы поняли».

>Не нужно в этом месте придумывать велосипед.
т.е. Вы назвали целый блок паттернов велосипедом. угу…
UFO just landed and posted this here
>И в C# нет Multiple dispatch, поэтому используется Visitor (как бы вы его не называли, т.к. double dispatch в C# делается через Visitor)

предлагаю на этом закончить дискуссию :)
UFO just landed and posted this here
нет, я не считаю, что с появлением dynamic в C# появился multiple dispatch.

вышеназванный шаблон прекрасно эмулируется через if, switch, visitor pattern в C, Java, C++ и т.д. даже reflection подойдет как крайний случай.
но обычно предполагается, что visitor является примером решения/частным случаем самого double dispatch и обычно используется для отделения данных от алгоритмов.
Типичным примером для visitor'a является (как Вы сами прекрасно знаете) обход какого-либо набора данных, скажем AST.
как и в случае с примером из статьи, вместо CollideWith — Visit/Accept. плюсом является использование принципа открытости/закрытости.

НО, visitor — это поведенческий шаблон, в то время как double/multiple dispatch являются такими же атрибутами полиморфизма как и параметрический полиморфизм (aka обобщения aka generics).
Ведь оба вводят понятие динамической диспетчеризации в пику single dispatch.

Почему double dispatch — частный случай multiple dispatch?
Потому что он (т.е. double dispatch) эмулирует последний (multiple dispatch), используя single dispatch. выбор перегрузки осуществляется на этапе компиляции, не так ли?

т.о., multiple dispatch является понятием более широким, чем double dispatch.
visitor, в свою очередь, является лишь примером решения/реализации double dispatch. в ситуации, когда сущности и их поведение сильно сплетены, то, увы, даже большой с натяжкой будет трудно это назвать visitor'ом. называть это можно будет как угодно, но double dispatch будет.

p.s.
надеюсь, что мы не потеряли еще одну важную деталь: double dispatch оперирует с 2-мя аргументами (где 1-й сам экземпляр, например), а multiple dispatch — со всеми аргументами метода, что только подтверждает умозаключения в статье.
UFO just landed and posted this here
ага! поинт понятен теперь. ну да, от перестановки слагаемых сумма не меняется ;)

p.s.
наверное, имели в виду статическую типизацию, ибо тот же Python — язык динамический, но со строгой типизацией.
UFO just landed and posted this here
Sign up to leave a comment.

Articles

Change theme settings