Может быть вы меня не так поняли, я имел ввиду что когда мы пытаемся упаковать в конкретный транспорт разные типы сообщений(которые приходят в виде обобщенного класса/интерфейса) я не вижу способа избежать typeof. А если пытаться впихнуть детали транспорта пусть даже в DTO, то во-первых, специфичный код транспорта начинает размазываться, а во вторых с увеличение числа различных транспортов эти DTO начинают превращаться в свалку.
Мне как-то не очень нравится вариант с внесением специфичной для транспорта логики в DTO и для меня остается вариант с typeof, что мне очень не нравится.
Double dispatch в лице Visitor позволяет решить проблему typeof и загрязнения DTO деталями транспорта. Вся транспорто-специфичная логика уходит в Visitor.
Хотел уточнить у вас какой подход вы практикуете для работы с объектами разных классов с разными интерфейсами?
Например, у нас есть сервис, который получает команды от пользователя и отдает ему ответ. Притом сами запросы и ответы статичны и определены в доменной модели, меняется всего лишь транспорт доставки ответа клиенту. Хотелось бы чтобы доменная модель при возврате ответа играла роль Producer, а конкретный транспорт Consumer. И хотелось бы чтобы Producer генерировал «рабочие элементы» в каком-то обобщенном виде. В таком случае Consumer видит только некий базовый класс/интерфейс ClientResponseBase. И хотелось бы не смешивать иерархию ClientResponseBase с конкретными транспортами. И Vizitor имеет свои плюсы, есть возможность запихнуть всю логику упаковки всех вариантов ответа для одного конкретного транспорта в один класс и она больше не всплывет нигде.
Если не сложно поделитесь опытом решения схожих задач.
Я извиняюсь за приступ некрофилии, мне кажется что в вашем случае это не double dispatch. Как гласит Википедия
In software engineering, double dispatch is a special form of multiple dispatch, and a mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call.
У вас же все типы объектов известны на стадии компиляции. Диспетчеризацией и не пахнет. В англоязычной вики есть простой пример именно double dispatching.
Не-не, вы меня как-то не так поняли. В заголовке моей статьи не было фраз типа «80% продакшн кода написано некорректно» или чего-то похожего. Эту статью можно отнести к категории Tips & Tricks, или даже как пример вопроса на собеседовании на предмет определения уровня понимания дотнета. Меня позабавил тот факт, что ответив на автомате сам себе на «вопрос» из книги, я ошибся, вот и решил поделиться «радостью».
Да это все понятно, но сама система «растет» уже лет эдак 8, представляете какое это наследие? Да и работаем мы с кучей сторонних процессингов, терминалов. Вот так взять и запилить фичу это дороговато будет. Тут как в бойцовском клубе: пока сумма выплат по страховке меньше стоимости отзыва серии автомобилей — делать ничего не будем.
Как раз работаю в похожей системе, и у нас есть возможность посмотреть статус платежа по ID, который, можно сказать, инкрементируется, т.е. легко подбираем.
Но это диктуется удобством пользователя, если Вы положили деньги в терминале и хотите узнать статус платежа — то Вы можете просто зайти на сайт и по номеру чека получит эту инфу. Удобно. Никаких регистраций. Да и стали бы Вы регистрироваться на сайте владельца терминала только для того, чтобы узнать статус платежа.
С другой стороны есть проблемы с безопасностью(причем, с точки зрения менеджеров, незначительные). Не могу сказать как у нас принимали окончательное решение об этой фиче, какие варианты рассматривали, но работает оно так.
Ну да, делов-то, ведь уже, аж, целых два года. Вполне может белыми Королевский гамбит гонять, а черными контратаку Маршалла со старушечкой. Если мне не изменяет память ни один из чемпионов мира(каждый из которых своего рода гений) не начал играть в столь юном возрасте.
Насчет сервиса платежей, думал также как Вы пока не устроился работать в один из них. Это был достаточно крупный «сервис», но качество кода меня сильно удивило. Там были и методы 300+ строк, методы поменьше, но с количеством условных конструкций под 40-50. Кучи спагетти-кода. Просто магические или даже загадочные места. В плюс ко всему была куча мест которые алгоритм проведения платежа реализовывали по-разному и правильных вариантов было очень мало. Я это к тому, что МОЯ практика показывает, что говнокода полно и в «сервисах по загрузке кода» и в «платежных системах», а любую надежность можно обеспечить человеческим фактором. Не прошел платеж? Сейчас наш программист вручную проверит что с ним случилось — и этом способ работает.
По памяти:
Вы можете даже вообще эту сборку выкинуть из папки(только в том случае, когда из сборки используется только эта константа) — сборка даже не будет подгружаться. Соответственно, после компиляции можете подменять эту сборку сколько захотите и как захотите.
Также(да, мне нечем заняться с утра :)), не контролируется длина полей ввода. Если вводить в них достаточно длинные строки, то выводятся ошибки.
Например, вот тут:
var error = deserializer.Deserialize<YandexError>(response);
throw new YandexLinguisticsException(error);
Метод Deserialize может вернуть null(при длинной введенной строке), и в строке результата вижу:
System.NullReferenceException: Object reference not set to an instance of an object.
at YandexLinguistics.NET.YandexLinguisticsException..ctor(YandexError error) in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET\YandexLinguisticsException.cs:line 12
at YandexLinguistics.NET.Dictionary.Lookup(LangPair lang, String text, String ui, LookupOptions flags) in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET\Dictionary\Dictionary.cs:line 69
at YandexLinguistics.NET.Gui.frmMain.<UpdateDictionaryResult>b__13() in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET.Gui\frmMain.cs:line 332
public class Dictionary
{
protected RestClient _client = new RestClient("https://dictionary.yandex.net/api/v1/dicservice/");
protected string _key;
...
}
В классе у которого нет наследников.
А если брать глобальнее, то:
1. Отсутствие асинхронного API
2. Куча повторяющихся кусков кода, как то:
RestRequest request = new RestRequest("getLangs");
request.AddParameter("key", _key);
RestResponse response = (RestResponse)_client.Execute(request);
XmlAttributeDeserializer deserializer = new XmlAttributeDeserializer();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var strs = deserializer.Deserialize<List<string>>(response);
var allLangs = (Lang[])Enum.GetValues(typeof(Lang));
Lang[] result = allLangs.Where(lang => strs.Contains(lang.ToString().ToLowerInvariant())).ToArray();
return result;
}
else
{
var error = deserializer.Deserialize<YandexError>(response);
throw new YandexLinguisticsException(error);
}
Или вот такой паттерн, встречается многократно
RestResponse response = (RestResponse)_client.Execute(request);
XmlAttributeDeserializer deserializer = new XmlAttributeDeserializer();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
...
}
else
{
...
}
Это все, явные кандидаты на какое-то абстрагирование.
3. Хоть гуевое приложение в «наггет» не пойдет, но в нем все сетевые вызовы синхронны.
Там не используется LinkedList, способ внутреннего хранения таймеров схож с работой LinkedList. Раз такой интерес к этому способу хранения, я допишу об этом позже.
PS. веткой промазал.
Вот хоть убейте, в чем проблема?
Мне как-то не очень нравится вариант с внесением специфичной для транспорта логики в DTO и для меня остается вариант с typeof, что мне очень не нравится.
Double dispatch в лице Visitor позволяет решить проблему typeof и загрязнения DTO деталями транспорта. Вся транспорто-специфичная логика уходит в Visitor.
Например, у нас есть сервис, который получает команды от пользователя и отдает ему ответ. Притом сами запросы и ответы статичны и определены в доменной модели, меняется всего лишь транспорт доставки ответа клиенту. Хотелось бы чтобы доменная модель при возврате ответа играла роль Producer, а конкретный транспорт Consumer. И хотелось бы чтобы Producer генерировал «рабочие элементы» в каком-то обобщенном виде. В таком случае Consumer видит только некий базовый класс/интерфейс ClientResponseBase. И хотелось бы не смешивать иерархию ClientResponseBase с конкретными транспортами. И Vizitor имеет свои плюсы, есть возможность запихнуть всю логику упаковки всех вариантов ответа для одного конкретного транспорта в один класс и она больше не всплывет нигде.
Если не сложно поделитесь опытом решения схожих задач.
У вас же все типы объектов известны на стадии компиляции. Диспетчеризацией и не пахнет. В англоязычной вики есть простой пример именно double dispatching.
Цель статьи всего лишь показать грабли на которые можно наступить.
Но это диктуется удобством пользователя, если Вы положили деньги в терминале и хотите узнать статус платежа — то Вы можете просто зайти на сайт и по номеру чека получит эту инфу. Удобно. Никаких регистраций. Да и стали бы Вы регистрироваться на сайте владельца терминала только для того, чтобы узнать статус платежа.
С другой стороны есть проблемы с безопасностью(причем, с точки зрения менеджеров, незначительные). Не могу сказать как у нас принимали окончательное решение об этой фиче, какие варианты рассматривали, но работает оно так.
Вы можете даже вообще эту сборку выкинуть из папки(только в том случае, когда из сборки используется только эта константа) — сборка даже не будет подгружаться. Соответственно, после компиляции можете подменять эту сборку сколько захотите и как захотите.
Например, вот тут:
Метод Deserialize может вернуть null(при длинной введенной строке), и в строке результата вижу:
System.NullReferenceException: Object reference not set to an instance of an object. at YandexLinguistics.NET.YandexLinguisticsException..ctor(YandexError error) in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET\YandexLinguisticsException.cs:line 12 at YandexLinguistics.NET.Dictionary.Lookup(LangPair lang, String text, String ui, LookupOptions flags) in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET\Dictionary\Dictionary.cs:line 69 at YandexLinguistics.NET.Gui.frmMain.<UpdateDictionaryResult>b__13() in c:\Users\юленька\Documents\Code\Yandex-Linguistics.NET\YandexLinguistics.NET.Gui\frmMain.cs:line 332
Вообще выглядят странными конструкции вида:
В классе у которого нет наследников.
А если брать глобальнее, то:
1. Отсутствие асинхронного API
2. Куча повторяющихся кусков кода, как то:
Или вот такой паттерн, встречается многократно
Это все, явные кандидаты на какое-то абстрагирование.
3. Хоть гуевое приложение в «наггет» не пойдет, но в нем все сетевые вызовы синхронны.
Это происходит потому что компилятор «инлайнит» значения констант. У Рихтера можно почитать про разницу статических полей и констант.