Подтвердить что ты не робот

Событие, делегат или интерфейс?

Предположим, что у меня есть класс Monkey, который иногда должен приобретать экземпляр Банана. Способ, которым этот банан предоставляется, не представляет интереса обезьяны, но он инициирует получение бананов.

Теперь у меня есть как минимум три возможных способа подключения моей обезьяны к поставщику бананов. Каков наилучший способ сделать это?

1. Событие

Поднять событие Monkey.BananaNeeded. Обработчик событий устанавливает свойство BananaNeededEventArgs.Banana.

2. Интерфейс

Вызвать IBananaProvider.GetBanana. Экземпляр IBananaProvider вводится в обезьяне как аргумент конструктора или через свойство.

3. Делегат

Вызовите делегата типа System.Func<Banana>. Делегат вводится в обезьяну как аргумент конструктора или через свойство. Это заманчиво, потому что это не требует объявления каких-либо дополнительных интерфейсов или классов, но, по-видимому, это не популярный выбор.

4b9b3361

Ответ 1

Мне не нравятся параметры события и делегата, если нет механизма для обеспечения того, чтобы несколько обработчиков не были прикреплены. Таким образом, вариант 2 является победителем, ИМО.

Ответ 2

Модель событий работает лучше всего, если есть несколько возможных поставщиков бананов, то есть обезьяна просит всех поставщиков в свою очередь о банане, а первая, которая может предоставить, позволяет доставить ее.

Другие две модели отлично работают для одного бананового промаха на обезьяну. Делегату проще создать поставщика, а интерфейс более структурирован.

Ответ 3

Лучший способ в приведенном выше сценарии - перейти к интерфейсу. В терминах OOAD мы можем определить вышеупомянутый сценарий, поскольку класс Monkey имеет банану.

Ответ 4

Я обычно использую 2.

IBananaProvider bananaProvider = ProviderFactory.Get<IBananaProvider>();

Ответ 5

Есть операции над Monkey, которые требуют от него банана.

Необходимость обезьяны для получения банана подходит для этих операций. Вы можете с уверенностью передать Банану в качестве параметра для операций.

Я удалю зависимость денег от IBananaProvider.

class Monkey
{
   void FeedWith(Banana banana) { ... }
}

Это будет отвратительность вызывающего, чтобы получить банан, а не обезьяну.

Я выступаю за то, чтобы не вводить услуги в сущности.

Ответ 6

Идеальная инъекция зависимостей - это более общепринятый подход.

Другим подходом к попытке является очередь сообщений.

В этом случае Monkey может по существу "отправить" сообщение в какую-то очередь для вкусного банана. Эта "проводка" запускает некоторого провайдера, который отслеживает очередь, чтобы затем выполнить запрос, который фактический экземпляр поставщика отвечает, не имеет значения. Monkey будет ждать ответа на исходное сообщение и предпримет действие, когда пакет, представляющий интерес, поступит из BananaProvider.

Очереди сообщений отключают поставщиков и потребителей. Конечно, есть еще какая-то форма контракта "на месте", но кто на другом конце запроса и ответа не имеет значения.

Ответ 7

Я бы согласился и сказал, что вариант 2 - лучшее решение. Делегаты, как правило, предназначены для того, чтобы делегировать что-то, другими словами, что-то делать, а события предназначены для немедленного ответа на то, что произошло, как правило, связанное с пользователем. Интерфейс просто говорит: "Это здесь, и вы можете это сделать". Поскольку вы не хотите, чтобы банан или обезьяна делали что-то сначала, и вы не хотите, чтобы пользователь напрямую взаимодействовал с внутренней работой вашего кода, интерфейс выигрывает. Все, что вам нужно, это знать, что обезьяна может получать бананы. То, что он решает делать с этими бананами после этого, зависит от него, и сам банан, а также окружающий код не должны касаться его.