Russian Qt Forum
Ноябрь 23, 2024, 03:32 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: [C++] Паттерн Посредник (Mediator) с передачей параметров  (Прочитано 7195 раз)
AlphaGh0St
Гость
« : Май 22, 2015, 09:34 »

Всем привет!
На досуге чуть по чуть изучаю паттерны проектирования, и заинтересовался паттерном Посредник.
Если взглянуть на UML-диаграмму паттерна Посредник, которая представлена далее, то можно заметить, что передача команды и получение ответа не подразумевает передачу параметра.


И я задался вопросом: а что если помимо самой команды нужно передать какие-то данные в качестве аргументов? А что если отправив команду, нужно получить ответ с каким-то параметрами?


Реализовал паттерн Посредник, добавил возможность передачи команд с аргументами, получение на них ответов (так же с аргументами). Команды и ответы задаются произвольно, параметры для них - тоже.
Иерархическая схема реализованного паттерна представлена далее.
IMediator - базовый класс посредника
ColleaguesMediator - конкретный класс посредника
IColleague - базовый класс коллеги
ColleagueA и ColleagueB - классы конкретных коллег
IInternalRequest - базовый класс команд и ответов
IInternalRequest и ConcreteResponseA - классы конкретной команды и ответа



Логика работы паттерна представлена далее. Из рисунка видно, что КоллегаА отправляет ЗапросА для КоллегиВ через Посредника. КоллегаВ получив запрос, отправляет ОтветА через Посредника для КоллегиА



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

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


// IMediator.h
Код:
#ifndef IMediator_h__
#define IMediator_h__
//-----------------------------------------------------------------------------------------------------------

#include "IColleague.h"
#include "InternalRequest.h"
//-----------------------------------------------------------------------------------------------------------

class IColleague;
class IInternalRequest;
//-----------------------------------------------------------------------------------------------------------

// Базовый класс посредник
class IMediator
{
public:
IMediator() { }
virtual ~IMediator() { }

virtual void RegisterColleague(IColleague* colleague) = 0; // Добавление коллеги
virtual void UnregisterColleague(IColleague* colleague) = 0; // Удаление коллеги

virtual void Send(IInternalRequest* cmd) = 0; // Отправка команды коллеге
};
//-----------------------------------------------------------------------------------------------------------

#endif // IMediator_h__


// ColleaguesMediator.h
Код:
#ifndef ColleaguesMediator_h__
#define ColleaguesMediator_h__
//-----------------------------------------------------------------------------------------------------------

#include "IMediator.h"
#include "IColleague.h"
#include <list>
//-----------------------------------------------------------------------------------------------------------

class ColleaguesMediator : public IMediator
{
public:
ColleaguesMediator() : IMediator() { }
virtual ~ColleaguesMediator() { m_Colleagues.clear(); }

// Добавление коллеги
virtual void RegisterColleague(IColleague* colleague) override { m_Colleagues.push_back(colleague); }

// Удаление коллеги
virtual void UnregisterColleague(IColleague* colleague) override
{
auto it = std::find(m_Colleagues.begin(), m_Colleagues.end(), colleague);
if (it != m_Colleagues.end())
m_Colleagues.erase(it);
}

// Отправка команды коллеге
virtual void Send(IInternalRequest* cmd) override
{
// Поиск коллеги по указанному в команде получателю и отправка ему команды
auto it = std::find(m_Colleagues.begin(), m_Colleagues.end(), cmd->GetReceiver());
if (it != m_Colleagues.end())
(*it)->RecvResponse(cmd);
}

private:
std::list<IColleague*> m_Colleagues; // Список коллег
};
//-----------------------------------------------------------------------------------------------------------

#endif // ColleaguesMediator_h


// IColleague.h
Код:
#ifndef IColleague_h__
#define IColleague_h__
//-----------------------------------------------------------------------------------------------------------

#include "IMediator.h"
#include "InternalRequest.h"
#include <list>
#include <memory>
//-----------------------------------------------------------------------------------------------------------

class IMediator;
//-----------------------------------------------------------------------------------------------------------

// Базовый класс коллега
// Чтобы добавить нового коллегу, необходимо описать класс-наследник от класса IColleague
// Переопределить чисто виртуальные методы SendRequest() и RecvResponse()
// И реализовать отправку запросов и получение, разбор, обработку ответов
class IColleague
{
public:
IColleague(IMediator* mediator) : m_Mediator(mediator) { }
virtual ~IColleague() { m_RequestQueue.clear(); }

virtual void SendRequest(IInternalRequest* cmd) = 0; // Отправка запроса через посредника
virtual void RecvResponse(IInternalRequest* cmd) = 0; // Получение ответа через посредника

protected:
// Сохраняется ID каждого отправленного запроса, чтобы знать
// Какие запросы были отправлены, и на какие ожидать ответы
void AddRequestToQueue(int request_id) { m_RequestQueue.push_back(request_id); }

// По получению ответа на запрос, ID запроса из списка удаляется
void DelRequestFromQueue(int request_id)
{
auto it = std::find(m_RequestQueue.begin(), m_RequestQueue.end(), request_id);
if (it != m_RequestQueue.end())
m_RequestQueue.erase(it);
}

// Проверка, ожидаем ли ответ по указанному ID
bool IsRequestInQueue(int request_id) const
{
auto it = std::find(m_RequestQueue.begin(), m_RequestQueue.end(), request_id);
if (it != m_RequestQueue.end())
return true;

return false;
}

protected:
IMediator* m_Mediator;

private:
std::list<int> m_RequestQueue; // Очередь запросов, содержащая ID запроса
};
//-----------------------------------------------------------------------------------------------------------

#endif // IColleague_h__


// ColleagueA.h
Код:
#ifndef ColleagueA_h__
#define ColleagueA_h__
//-----------------------------------------------------------------------------------------------------------

#include "IColleague.h"
#include "InternalResponse.h"
//-----------------------------------------------------------------------------------------------------------

class ColleagueA : public IColleague
{
public:
// В конструкторе добавляем посредника
// В деструкторе отписываемся от него
ColleagueA(IMediator* mediator) : IColleague(mediator) { }
virtual ~ColleagueA() { m_Mediator->UnregisterColleague(this); }

// Отправка запроса через посредника
virtual void SendRequest(IInternalRequest* cmd) override
{
// Если отправляем запрос, помещаем ID запроса в список
switch (cmd->GetTypeID())
{
case TypeID::RequestA:
AddRequestToQueue(cmd->GetID());
break;
}

cmd->SetSender(this); // Указываем себя в качестве отправителя
m_Mediator->Send(cmd); // Отправляем команду посреднику
}

// Получение ответа через посредника
virtual void RecvResponse(IInternalRequest* cmd) override
{
// Если запрос таким ID был отправлен и мы ожидаем ответ
// С таким же ID, значит, обработаем, как ответ
if (IsRequestInQueue(cmd->GetID()))
{
// Обработка ответа
switch (cmd->GetTypeID())
{
case TypeID::ResponseA:
{
auto c = dynamic_cast<ConcreteResponseA*>(cmd);
auto p = c->GetParam(); // Получение параметра ответа
ProcessResponseA(&p); // Какая-нибудь обработка

break;
}
}

// Ответ получили, ID из списка удаляем
DelRequestFromQueue(cmd->GetID());
}
else
{
// Обработка запроса
}
}

private:
// Какая-нибудь обработка
void ProcessResponseA(int* value) { *value += 10; }
};
//-----------------------------------------------------------------------------------------------------------

#endif // ColleagueA_h__


// ColleagueB.h
Код:
#ifndef ColleagueB_h__
#define ColleagueB_h__
//-----------------------------------------------------------------------------------------------------------

#include "IColleague.h"
//-----------------------------------------------------------------------------------------------------------

class ColleagueB : public IColleague
{
public:
// В конструкторе добавляем посредника
// В деструкторе отписываемся от него
ColleagueB(IMediator* mediator) : IColleague(mediator) { }
virtual ~ColleagueB() { m_Mediator->UnregisterColleague(this); }

// Отправка запроса через посредника
virtual void SendRequest(IInternalRequest* cmd) override
{
/*
switch (cmd->GetTypeID())
{
case TypeID::RequestA:
AddRequestToQueue(cmd->GetID());
break;
}
*/

cmd->SetSender(this); // Указываем себя в качестве отправителя
m_Mediator->Send(cmd); // Отправляем команду посреднику
}

// Получение ответа через посредника
virtual void RecvResponse(IInternalRequest* cmd) override
{
/*
if (IsRequestInQueue(cmd->GetID()))
{
// Обработка ответа

//

DelRequestFromQueue(cmd->GetID());
}
else
*/
{
// Обработка запроса
switch (cmd->GetTypeID())
{
case TypeID::RequestA:
{
auto c = dynamic_cast<ConcreteRequestA*>(cmd);
auto p = c->GetParam(); // Получение параметра запроса
ProcessCommandA(&p); // Какая-нибудь обработка

// Формирование и отправка ответа на запрос
// В ответе указываем получателя, который был отправителем запроса (return to sender)
// ИД запроса и параметр ответа
std::shared_ptr<IInternalRequest> response(new ConcreteResponseA(cmd->GetSender(), cmd->GetID(), p));
SendRequest(response.get()); // Отправляем ответ так же через посредника

break;
}
}
}
}

private:
// Какая-нибудь обработка
void ProcessCommandA(int* value) { *value *= 2; }
};
//-----------------------------------------------------------------------------------------------------------

#endif // ColleagueB_h__


// InternalRequest.h
Код:
#ifndef InternalRequest_h__
#define InternalRequest_h__
//-----------------------------------------------------------------------------------------------------------

#include "IColleague.h"
#include <ctime>
#include <cstdlib>
//-----------------------------------------------------------------------------------------------------------

class IColleague;
//-----------------------------------------------------------------------------------------------------------

// Тип запроса/ответа
// Для добавления новой команды, достаточно добавить ИД типа для неё
// Затем описать класс-наследник от IInternalRequest
// Аналогично с добавлением нового ответа
enum class TypeID
{
RequestA  = 0, // Запрос А
ResponseA = 1  // Ответ А
};
//-----------------------------------------------------------------------------------------------------------

// Базовый класс команды от которого наследуются классы запросов и ответов
class IInternalRequest
{
public:
// Принимает получателя, ИД типа и ИД запроса
// ИД типа - это поле TypeID, идентифицирующее команду или ответ на неё
// ИД запроса - уникальный идентификатор конкретного запроса
IInternalRequest(IColleague* reciever, TypeID type_id, int id = 0) :
m_Sender(nullptr),
m_Receiver(reciever),
m_TypeID(type_id),
m_ID(id)
{
if (!m_ID)
{
srand((unsigned)time(nullptr));
m_ID = rand();
}
}

virtual ~IInternalRequest() { m_Sender = m_Receiver = nullptr; }

void SetSender(IColleague* sender)     { m_Sender   = sender;   }
void SetReceiver(IColleague* receiver) { m_Receiver = receiver; }

IColleague* GetSender() const   { return m_Sender;   }
IColleague* GetReceiver() const { return m_Receiver; }

TypeID GetTypeID() const { return m_TypeID; }
int    GetID() const     { return m_ID;     }

private:
IColleague* m_Sender;
IColleague* m_Receiver;
TypeID      m_TypeID;
int         m_ID;
};
//-----------------------------------------------------------------------------------------------------------


// InternalResponse.h
Код:
#ifndef InternalResponse_h__
#define InternalResponse_h__
//-----------------------------------------------------------------------------------------------------------

#include "InternalRequest.h"
//-----------------------------------------------------------------------------------------------------------

// Ответ на команду А
class ConcreteResponseA : public IInternalRequest
{
public:
// На вход принимает получателя ответа, ИД ответа и параметр ответа
// Устанавливает тип ответа ResponseA
// Возможность установки ИД ответа обусловлена тем, что ИД запроса должен быть равен ИД ответа
// И перед отправкой ответа, в качестве ИД передаётся ИД запроса
// Таким образом появляется возможность сопоставить запрос и ответ
ConcreteResponseA(IColleague* reciever, int id, int param) :
IInternalRequest(reciever, TypeID::ResponseA, id),
m_Param(param)
{
}

virtual ~ConcreteResponseA() { }

int GetParam() const { return m_Param; }

private:
int m_Param;
};
//-----------------------------------------------------------------------------------------------------------

#endif // InternalResponse_h__


// main.cpp
Код:
#include "ColleaguesMediator.h"
#include "ColleagueA.h"
#include "ColleagueB.h"
#include <memory>
//-----------------------------------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
// Создание посредника
std::shared_ptr<ColleaguesMediator> mediator(new ColleaguesMediator);

// Создание коллег
std::shared_ptr<ColleagueA> colleague_a(new ColleagueA(mediator.get()));
std::shared_ptr<ColleagueB> colleague_b(new ColleagueB(mediator.get()));

// Добавление коллег посреднику
mediator->RegisterColleague(colleague_a.get());
mediator->RegisterColleague(colleague_b.get());

// Формирование и отправка запроса из коллеги А коллеге В
std::shared_ptr<IInternalRequest> cmd(new ConcreteRequestA(colleague_b.get(), 12));
colleague_a->SendRequest(cmd.get());

return 0;
}
//-----------------------------------------------------------------------------------------------------------
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Май 22, 2015, 12:22 »

Цитировать
typedef struct tagMSG {
  HWND   hwnd;
  UINT   message;
  WPARAM wParam;
  LPARAM lParam;
  ...
}
Что общего у этого с тем что Вы рассказали? Да почти все. wParam добавьте, он удобен, во многих случаях удается отделаться только им (не создавая структуру в куче всякий раз)

Это я к тому что если вещь известна/стандартна, то не лучше ли и в реализации следовать "известным образцам" и (может даже важнее) известным терминам? А так Вы нагородили "нечто" - что не значит "плохо", но во что нужно вникать, прилагать усилия. А это мало кто будет делать - ведь на форум заходят отдохнуть и потрепаться, это нормально.

Немедленный ответ или постановка в очередь - это не имеет отношения к паттерну "медиатор". Вообще если связались с очередью - там много чего надо, это отдельный (и большой) разговор, Умейте разделять задачи.

Вообще заниматься "общими" вещами страшно приятно, эта болезнь поражает многих. Пока все в рамках абстрактных конструкций и "каких-то примеров" (любых, притянутых за уши) - все получается прекрасно. Но как только конкретная задача - всегда вылазит какая-то загогулина, противная мелочь которая намертво хоронит "красивую конструкцию"

Да, и если у Вас так много досуга - я найду чем его заполнить без всякого остатка (в личку)
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #2 : Май 22, 2015, 21:39 »

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

Регистрировать и разрегистрировать коллег у посредника удобней делать один раз в конструкторе и деструкторе IColleague соответсвенно. Это избавит вас делать это во всех наследниках коллег.


Записан
AlphaGh0St
Гость
« Ответ #3 : Май 24, 2015, 15:52 »

Спасибо за ответы! А можно подробнее о правильном использовании умных указателей? Может пример кода в качестве образца правильного и не правильного использования?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Май 24, 2015, 17:15 »

Лучше не смешивать умные и обычные указатели в одной программе.
Смотрите:
Код
C++ (Qt)
{
   shared_ptr<Obj> o( new Obj( par1, par2 ) );
   runFunc( o.get() );
}
 
Объект o разрушиться в конце блока и если функция runFunc сохраняет указатель на объект в какой-то коллекции, то в дальнейшем коллекция будет хранить указатель на уже разрушенный объект.
Через обычные указатели вы не можете контролировать время жизни объектов, поэтому если решили использовать умные указатели, то используйте их везде.

Для сильных связей (например, владелец хранит дочерние объекты) используйте shared_ptr, для слабых (например, мы хотим хранить указатель на объект, который не контролируем) - weak_ptr.
Хорошо продумывайте деревья владений (какой объект какими владеет (контролирует)), это позволит не создавать циклических сильных связей, при котором объекты могут никогда не разрушиться.

Используйте умные указатели в настоящих проектах и вы быстро с ними разберетесь.

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Май 25, 2015, 07:37 »

Код:
class IColleague
{
public:
IColleague(IMediator* mediator) : m_Mediator(mediator) { }
virtual ~IColleague() { m_RequestQueue.clear(); }

virtual void SendRequest(IInternalRequest* cmd) = 0; // Отправка запроса через посредника
1) Почему SendRequest - обязательный метод? Разве не возможен коллега/калека который ничего не посылает, (но умеет принимать)?

2) Почему m_Mediator  - член базового класса? Разве их не может быть несколько? (аналогии напрашиваются, напр подписка на неск форумов). Или наоборот ни одного (см 1)? Если же полагаем что всегда один, то честнее/яснее сделать его синглтоном. Даже если перенести его в конкретные классы коллег, то все равно m_Mediator - "плохой" указатель. Удалить/поменять его просто так нельзя, ведь коллеги его юзают. Правда он хранит список пользующих, по которому можно пробежаться. Ну это еще один наворот

3) Неясно откуда IColleague возьмет указатель на получателя. Если он ему явно известен, то зачем посредник? Как и в жизни, если продавец и покупатель лично знакомы, то часто обходятся без посредников. А вот если "в глаза не видел" - то вряд ли, посредник необходим.

4) Так и не понял схему работы - синхронно или асинхронно? Это должно задаваться явно, напр SendEvent/PostEvent, и определяться отправителем. И кто отвечает за удаление команды?

Пока хватит, если интересно - продолжим
Записан
AlphaGh0St
Гость
« Ответ #6 : Май 25, 2015, 12:15 »

Цитировать
1) Почему SendRequest - обязательный метод? Разве не возможен коллега/калека который ничего не посылает, (но умеет принимать)?
Обязательный потому, этот метод должен быть судя по диаграмме. Теоретически может быть такой коллега (он же калека). Т.е. Вы предлагаете не делать метод отправки обязательным?

Цитировать
2) Почему m_Mediator  - член базового класса? Разве их не может быть несколько? (аналогии напрашиваются, напр подписка на неск форумов). Или наоборот ни одного (см 1)? Если же полагаем что всегда один, то честнее/яснее сделать его синглтоном. Даже если перенести его в конкретные классы коллег, то все равно m_Mediator - "плохой" указатель. Удалить/поменять его просто так нельзя, ведь коллеги его юзают. Правда он хранит список пользующих, по которому можно пробежаться. Ну это еще один наворот
Опять же теоретически посредников может быть несколько, хотя изначально я об этом и не задумывался.  Вы предлагаете сделать так, чтобы можно было работать с несколькими посредниками? Тогда, что если хранить посредников в списке умных указателей и добавить методы по регистрации и удалении посредника?
Но тогда возникает вопрос: если будут несколько посредников и коллега пошлёт запрос, как узнать, через какого посредника отправлять этот запрос?

Цитировать
3) Неясно откуда IColleague возьмет указатель на получателя. Если он ему явно известен, то зачем посредник? Как и в жизни, если продавец и покупатель лично знакомы, то часто обходятся без посредников. А вот если "в глаза не видел" - то вряд ли, посредник необходим.
Изначально планировал сделать без указания получателя, но возникают вопросы. Т.е. коллега отправляет запрос  и ему всё равно, кто этот запрос будет обрабатывать, главное, чтобы обработали. Но, как тогда посредник поймёт, кому передавать запрос? Тут я вижу два варианта:
1) Найти в списке коллегу, который сможет обработать запрос и отдать запрос ему. А что если этот запрос могут обработать 2 коллеги и он предназначался второму? Вот и проблема, запрос выполнит первый коллега из списка а второй о запросе ничего и не узнает.
2) Отправлять запрос всем коллегами из списка, а каждый коллега проверит, может ли он обработать запрос. Если может - обработает. Но и тут проблема, если запрос предназначался только одному коллеге, а второй о нём знать не должен, то в итоге запрос обработают оба.
В общем, выбрал меньшее зло в виде явно указываемого получателя. Возможно, у Вас есть идеи по этому поводу?

Цитировать
4) Так и не понял схему работы - синхронно или асинхронно? Это должно задаваться явно, напр SendEvent/PostEvent, и определяться отправителем. И кто отвечает за удаление команды?
Схема работы - синхронная, всё в одном потоке. Хотя, возможно и следовало бы предусмотреть асинхронный вариант.
1) Какая разница между SendEvent и PostEvent?
Если имеются в виду методы Qt, то паттерн пишу без использования Qt. Но ведь даже в Qt указывается получатель.
Если ли смысл реализовывать аналоги?

2) В каком случае следует делать синхронную работу, а в каких асинхронную?
3) О каком удалении команды Вы говорите?
« Последнее редактирование: Май 25, 2015, 12:33 от AlphaGh0St » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Май 25, 2015, 13:15 »

Схема работы - синхронная, всё в одном потоке. Хотя, возможно и следовало бы предусмотреть асинхронный вариант.
1) Какая разница между SendEvent и PostEvent?
Если имеются в виду методы Qt, то паттерн пишу без использования Qt. Но ведь даже в Qt указывается получатель.
Если ли смысл реализовывать аналоги?

2) В каком случае следует делать синхронную работу, а в каких асинхронную?
3) О каком удалении команды Вы говорите?
"Синхронно" означает что когда (после вызова) управление вернулось, то все уже свершилось, результат готов (удачный или нет). "Асинхронно" значит что запрос "принят к исполнению" (часто поставлен в очередь) но когда выполнится и придут результаты - хз. Кол-во потоков здесь ни при чем.

SendEvent и PostEvent - ф-ции WinAPI (синхронный и асинхронный вызовы). В Qt это методы QApplication (называются так же только с маленькой буквы) с примерно тем же ф-ционалом. Разделение методов (Send, Recv) обязательно для асинхронной работы - но совершенно напрасный геморрой для синхронной. Обычно при синхронном вызове команда удаляется отправителем ("я создал - я и удалил"). А при асинхронном всяко, часто самой очередью - ведь до ответа отправителю дело может и не дойти. У Вас не вижу кто вообще удаляет InternalRequest

На остальное отвечу позже, надо подумать
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Май 25, 2015, 17:07 »

Т.е. Вы предлагаете не делать метод отправки обязательным?
Нет никаких обязательных методов.
Основной посыл этого приема заключается в том, что мы можем организовать взаимодействия множества объектов через один объект посредник и по его правилам. Т.е. если объектов тысячи, нам не нужно в каждом хранить указатели на все остальные объекты - их будет знать посредник. Так же он определяет сам интерфейс и поведение всего взаимодействия. Каждому объекту будет достаточно знать только самого посредника и не обязательно знать все разнообразие и интерфейсы других объектов.
Например, у нас есть тысячи объектов разных датчиков, которые могут сообщать друг другу свои состояния. И есть класс посредник, которые знает как обрабатывать их взаимодействие.
Код
C++ (Qt)
class SensorBase;
 
class Manager
{
public:
   Manager() {}
 
   void addSensor( SensorBase *sensor )
   {
       m_sensors.push_back( sensor );
   }
 
   // Методы определяющие интерфейс взаимодействия
 
   // Сенсор отвалился
   void    alarm( SensorBase *sensor );
 
   // Изменилось значение сенсора
   void    setValue( SensorBase *sensor, int value );
 
private:
   list<SensorBase*>    m_sensors;
}
 
class SensorBase
{
public:
   SensorBase( Manager *manager ) : m_manager( manager ) {}
 
protected:
   Manager    *m_manager;
}
 
class Sensor : public SensorBase
{
public:
   Sensor( Manager *manager ) : SensorBase( manager ) {}
 
   void error()
   {
       m_manager->alarm( this );
   }
 
   void valueChange()
   {
       m_manager->setValue( this, 100500 );
   }
}
 

Что будет происходить в методах Manager::alarm и Manager::setValue это детали реализации. Менеджер может вызывать методы других сенсоров для информирования их о изменении состояния одного из сенсоров, он может сохранять состояния сенсоров в других объектах-хранилищах, передавать эти значения третьим объектам принимающим решения и т.д.

Главное, что объекты ничего не знают о других объектах и все общение сводиться через один объект-посредник, который и описывает все разнообразие этого общения.

Но тогда возникает вопрос: если будут несколько посредников и коллега пошлёт запрос, как узнать, через какого посредника отправлять этот запрос?
Посредников может быть несколько и они могут иметь разный интерфейс. Вам нужно хранить указатели на каждый из посредников в классах коллегах и использовать их методы для взаимодействия.

Но, как тогда посредник поймёт, кому передавать запрос?
Это все тонкости реализации конкретного посредника. Никто не мешает сделать у посредника метод, который будет подбирать по каким-то критериям объект-коллегу, который может обрабатывать какие-то запросы другого коллеги.
« Последнее редактирование: Май 25, 2015, 17:09 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Май 26, 2015, 08:57 »

Обязательный потому, этот метод должен быть судя по диаграмме.
Улыбающийся

1) Найти в списке коллегу, который сможет ..
2) Отправлять запрос всем коллегами из списка, а каждый ...
Это все возможные варианты - и найдется еще больше. Ваша реализация вполне разумна, но ни на какую общность не претендует. Вообще следование диаграммам не всегда хорошо. Напр утверждение "есть базовый класс "коллега", все должны быть его наследниками". Это не так уж бесспорно. В жизни редко бывает "пример с нуля", обычно уже есть какие-то (часто работающие) классы. Конечно можно и к ним добавить "коллегу" множественным наследованием, но желания так сделать почему-то не возникает. У того коллеги ф-ционала с гулькин нос, и этот ф-ционал типовой (register/unregister/receive). В конкретной задаче я бы думал как обойтись вообще без класса "коллега" используя напр слоты/сигналы, базовые возможности QObject, глянул бы QSignalMapper.  А без задачи можно нагородить что угодно - и все пройдет, ведь никаких критериев оценки нет. Но ценность этого невелика.
« Последнее редактирование: Май 26, 2015, 08:59 от Igors » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.105 секунд. Запросов: 23.