Модель спостерігача; знаючи * що * змінилося?


10

Я створив два абстрактних класи Subject і 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. Чи спостерігач навіть найкращий зразок? Можливо, якась система заходів?

ОНОВЛЕННЯ

Я знайшов цю статтю, в якій йдеться про шаблони шаблону спостерігача: реалізація шаблону тема / спостерігача за допомогою шаблонів . Це змусило мене замислитися, чи можете ви викласти аргумент.

Я знайшов це запитання про переповнення стека, в якому йдеться про шаблону аргументу: Шаблон тематичного спостерігача на основі шаблону - чи слід використовувати static_cast або dynam_cast . Однак у ОП, здається, є проблема, на яку ніхто не відповів.

Інша річ, яку я можу зробити, - це змінити метод оновлення, щоб взяти об’єкт 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 тип даних не жорстко між observee і спостерігачем - ще це зчеплення , що робить речі важко розвиватися.

EDIT
Якщо ви хочете, щоб цей спостерігач не тільки спостерігав, але й потребував виконання багатьох інших завдань на основі того, що змінилося тоді, ви також можете перевірити шаблон відвідувачів . У шаблоні відвідувача спостерігач / відвідувач викликає модифікований об'єкт, а отже, може не тільки знати, що таке модифікація, але й може насправді працювати на ній


5
Якщо спостерігач інтерпретує аргумент, то є муфта, незалежно від того , як ви приховати тип. Насправді я б сказав , що це більш важко розвиватися , якщо ви проходите Object( void *, boost::anyабо що - то так само родове) , ніж якби ви передати конкретний тип, тому що з типом конкретного ви побачите під час компіляції , що - щось змінилося, в той час як із загальним типом його складе та припинить роботу, оскільки спостерігач не зможе працювати з фактично переданими даними.
Ян Худек

@JanHudec: Я згоден з цим, але чи це означає, що ви створюєте для кожного аргументу певний одноразовий підклас спостерігача / суб'єкта (тобто для кожного випадку використання)?
Користувач

@JanHudec: також з'єднання є лише одним способом. Суб'єкт не має уявлення про спостерігачів. Так, спостерігач знає про цю тему, але чи не так працює модель спостерігача?
Користувач

1
@User: Так, я створюю специфічний інтерфейс для кожного предмета, і кожен спостерігач реалізує інтерфейси предметів, які він повинен спостерігати. Що ж, усі мови, якими я користуюся, мають вказівник методу на мові чи рамках (C # делегати, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, методи, пов'язані з python тощо), тому я просто визначаю методи з відповідними підписами та прошу систему створити фактичну спостерігач. Зв'язок - це дійсно лише один спосіб, суб'єкт визначає інтерфейс для спостерігачів, але не має уявлення про їх реалізацію.
Ян Худек

0

Існує кілька способів надіслати загальний аргумент події "Java Like" в C ++.

1) Оголосити аргумент події недійсним * та передати його правильному класу в обробнику події.

2) Заявіть аргумент події як вказівник / ref на новий клас / інтерфейс, такий як (у відповідь на ваш приклад)

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)

Мінус: спостерігачу, можливо, доведеться попросити спостережувану для отримання додаткової інформації (тобто, ваше "ціле число")

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.