Проблема с этой архитектурой заключается в том, что когда мы реализуем новую службу отправителя, мы также вынуждены модифицировать MessageDispatcher.
Возможно, это хорошо, поскольку компилятор заставит вас добавлять отправителя при создании нового сообщения.
Можно ли считать это нарушением принципа "открыть/закрыть"?
Да, но я бы рекомендовал прагматичный подход к принципу "открыто - закрыто". Хотя модификация необходима, класс очень прост и легко обновляется. И компилятор заставит вас сделать обновление, так что нет риска забыть.
Мне также не нравится, что IMessage содержит логику и больше не является простым DTO, и мы должны добавить больше сложности, чтобы избежать этого.
Я бы не стал сильно беспокоиться о такой тривиальной логике, как эта. Я бы снова предложил более прагматичный, чем догматичный взгляд. Если вы добавите больше сложности только для того, чтобы следовать догме, вы рискуете получить в итоге более сложное решение.
Есть ли другие методы, которые решают проблему более элегантным способом?
Отражение почти наверняка является альтернативой. Вы можете создать атрибут для ваших сообщений, что-то вроде [MessageSender(typeof(NotificationSender))]
, и использовать отражение для создания объекта отправителя во время выполнения.
Или вы можете использовать подход инъекции зависимостей, что-то вроде
var senderType = typeof(IMessageSender<>).MakeGenericType(new []{myMessageObject.GetType()});
myDiContainer.Resolve(senderType);
Или просто переключить тип сообщения, чтобы сопоставить его с соответствующим отправителем.
Но во всех этих решениях вы будете превращать ошибки времени компиляции в ошибки времени выполнения. И у вас будет риск забыть добавить соответствующего отправителя при добавлении нового типа сообщения. Это может не быть большой проблемой, если у вас есть соответствующее тестирование и т.д., но это компромисс, который необходимо учитывать.
Рекомендую посмотреть эти видео для лучшего погружения в вопрос: