Шаблон наблюдателя; зная * что * изменилось?

10

Я создал два абстрактных класса Subject и Observer, которые определяют классический интерфейс шаблонов Observer. Я извлекаю из них реализацию шаблона Observer. Наблюдатель может выглядеть так:

void MyClass::Update(Subject *subject)
{
    if(subject == myService_)
    {
        DoSomething();
    }
    else if(subject == myOtherService_)
    {
        DoSomethingElse();
    }
}

Это хорошо, и это говорит мне, кто что-то изменил. Однако, это не говорит мне, что изменилось. Иногда это нормально, потому что я просто собираюсь запросить у субъекта последние данные, но в других случаях мне нужно знать, что именно изменилось в предмете. Я заметил, что в Java есть метод notifyObservers () и метод notifyObservers (Object arg) для предположительного указания деталей об изменениях.

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

Итак, мои вопросы:

  1. Как в C ++ можно передать универсальный аргумент (как это делает Java)?
  2. Является ли Observer даже лучшим паттерном? Возможно, какая-то система событий?

ОБНОВИТЬ

Я нашел эту статью, в которой рассказывается о шаблоне шаблона Observer: реализация шаблона Subject / Observer с помощью шаблонов . Это заставило меня задуматься, не могли бы вы создать шаблон аргумента.

Я нашел этот вопрос переполнения стека, в котором говорится о шаблонировании аргумента: Шаблон Subject Observer на основе шаблона - должен ли я использовать static_cast или dynamic_cast . Однако у ОП, похоже, есть проблема, на которую никто не ответил.

Другая вещь, которую я мог бы сделать, это изменить метод Update, чтобы взять объект EventArg, как в:

void MyClass::Update(Subject *subject, EventArg arg)
{
  ...

А затем создайте подклассы EventArg для конкретных данных аргумента, а затем, я думаю, приведите их обратно к конкретному подклассу в методе обновления.

ОБНОВЛЕНИЕ 2

Также нашел статью, О создании асинхронных основ C ++ на основе сообщений; часть 2, в которой обсуждается, как субъект сообщает подробности о том, что изменилось.

Сейчас я серьезно думаю об использовании Boost.Signals . Использование моего собственного шаблона наблюдателя имело смысл, когда оно было простым, но шаблонирование типа и аргумента начинает усложняться. И мне может понадобиться безопасность потоков Boost.Signals2.

ОБНОВЛЕНИЕ 3

Я также нашел несколько интересных статей по шаблону наблюдателя:

Обобщающий наблюдатель Херб Саттер

Реализация шаблона наблюдателя в C ++ - Часть 1

Опыт реализации шаблона проектирования наблюдателя (часть 2)

Опыт реализации шаблона проектирования наблюдателя (часть 3)

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

пользователь
источник
Вопросы в теле не соответствуют вашему названию. Что действительно является ядром вопроса?
Николь
@Renesis: в настоящее время я использую шаблон Observer, как в примере кода в начале моего поста. Для кода, над которым я сейчас работаю, выясняется, что мне нужно точно знать, что изменилось, чтобы я мог реагировать соответствующим образом. Моя текущая реализация шаблона наблюдателя (который является стандартным) не предоставляет эту информацию. У меня вопрос, как лучше всего получить информацию о том, что изменилось.
Пользователь
@Renesis: вот ветка форума, задающая похожий вопрос на мой: gamedev.net/topic/497105-observer-pattern
пользователь
Обновление рекламы2: я определенно поддерживаю использование Boost.Signals. Это намного удобнее, чем кататься самостоятельно. Существует также libsigc ++, если вы хотели что-то более легкое для этой задачи (Boost.Signals использует много кода из остальной части Boost); это не потокобезопасный, хотя.
Ян Худек
Что касается проблем со скоростью: я не знаю точно, каковы быстрые Boost.Signals, но это вызывает беспокойство, когда у вас огромное количество событий, летающих вокруг ...
Макс.

Ответы:

4

Будь то C ++ или JAVA, уведомление для наблюдателя может прийти вместе с информацией о том, что изменилось. Те же методы notifyObservers (Object arg) также можно использовать и в C ++.

Как правило, проблема остается в том, что может быть несколько субъектов, отправляющих одному или нескольким наблюдателям, и, следовательно, class argих нельзя жестко закодировать.

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

Для шаблона наблюдателя, то есть важно , чтобы Arg тип данных не жёстко между observee и наблюдателем - еще это сцепление , что делает вещи трудно развиваться.

РЕДАКТИРОВАТЬ
Если вы хотите, чтобы наблюдатель не только наблюдал, но и выполнял множество других задач в зависимости от того, что изменилось , вы также можете проверить шаблон посетителя . В шаблоне посетитель наблюдатель / посетитель вызывает модифицированный объект и, следовательно, может не только знать, что такое модификация, но и реально работать с ним.

Дипан Мехта
источник
5
Если наблюдатель интерпретирует аргумент, то есть муфта, независимо от того , как вы скрыть тип. На самом деле я бы сказал , что это более трудно развиваться , если вы проходите Object( void *, boost::anyили что - то так же родовое) , чем если бы вы передать конкретный тип, потому что с типом конкретного вы увидите во время компиляции , что - то изменилось, в то время как с общим типом его скомпилируется и перестанет работать, потому что наблюдатель не сможет работать с фактическими переданными данными.
Ян Худек
@JanHudec: Я согласен с этим, но означает ли это, что вы устанавливаете определенный одноразовый подкласс Observer / Subject для каждого аргумента (т. Е. Для каждого варианта использования)?
Пользователь
@JanHudec: также связь только один путь. Субъект не имеет представления о наблюдателях. Да, наблюдатель знает о предмете, но разве не так работает шаблон наблюдателя?
Пользователь
1
@ Пользователь: Да, я делаю особый интерфейс для каждого субъекта, и каждый наблюдатель реализует интерфейсы предметов, которые он должен наблюдать. Все языки, которые я использую, имеют привязанные указатели методов в языке или среде (делегаты C #, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, методы с привязкой к Python и т. Д.), Поэтому я просто определяю методы с соответствующими сигнатурами и прошу систему создать фактические наблюдатель. Связывание действительно является только одним способом, субъект определяет интерфейс для наблюдателей, но не имеет представления об их реализации.
Ян Худек
0

Существует несколько способов отправки универсального аргумента события «Мне нравится» в C ++.

1) Объявите аргумент события как void * и приведите его к нужному классу в обработчике события.

2) Объявите аргумент события как указатель / ссылку на новый класс / интерфейс, например (в ответ на ваш пример)

class GenericEventArgument
{
  virtual bool didAction1Happen() = 0;
  virtual int getActionInteger() = 0;
};

И иметь фактические классы аргумента события, производные от этого класса.

Что касается вашего вопроса относительно шаблона на основе проверки наблюдателя

http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ

Гонен я
источник
-1

Я не знаю, является ли это каноническим именем, но из моих старых дней Smalltalk я помню термин «аспект», чтобы определить, что изменилось в наблюдаемом.

Это не так сложно, как ваша идея EventArg (и его подклассов); он просто передает строку (может быть даже целочисленной константой) от наблюдаемого к наблюдателю.

Плюс: есть только два простых метода ( update(observable)иupdateAspect(observable, aspect)

Минус: наблюдателю, возможно, придется запросить наблюдаемую дополнительную информацию (например, ваше «целое число»)

virtualnobi
источник