Название: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: AlphaGh0St от Май 22, 2015, 09:34 Всем привет!
На досуге чуть по чуть изучаю паттерны проектирования, и заинтересовался паттерном Посредник. Если взглянуть на UML-диаграмму паттерна Посредник, которая представлена далее, то можно заметить, что передача команды и получение ответа не подразумевает передачу параметра. (http://s019.radikal.ru/i637/1505/83/215e42830590.png) И я задался вопросом: а что если помимо самой команды нужно передать какие-то данные в качестве аргументов? А что если отправив команду, нужно получить ответ с каким-то параметрами? Реализовал паттерн Посредник, добавил возможность передачи команд с аргументами, получение на них ответов (так же с аргументами). Команды и ответы задаются произвольно, параметры для них - тоже. Иерархическая схема реализованного паттерна представлена далее. IMediator - базовый класс посредника ColleaguesMediator - конкретный класс посредника IColleague - базовый класс коллеги ColleagueA и ColleagueB - классы конкретных коллег IInternalRequest - базовый класс команд и ответов IInternalRequest и ConcreteResponseA - классы конкретной команды и ответа (http://i008.radikal.ru/1505/8e/2adebc391f67.png) Логика работы паттерна представлена далее. Из рисунка видно, что КоллегаА отправляет ЗапросА для КоллегиВ через Посредника. КоллегаВ получив запрос, отправляет ОтветА через Посредника для КоллегиА (http://s017.radikal.ru/i422/1505/40/b0e60ed97cbd.png) Далее предоставляю на всеобщее обозрение исходный код этой реализации. И, собственно, вопрос: я абсолютно уверен, что эта реализация далека от идеала, что-то сделано правильно, а что-то следует улучшить или убрать вовсе. В общем, выношу код на Ваш суд и готов выслушать любые предложения по улучшению кода. Меня этот вопрос заинтересовал. Возможно, заинтересует и Вас. Просто хочется довести реализацию до ума. Умышленно вынес весь код в заголовочные файлы, отформатировал код, и убрал некоторые проверки, чтобы сократить занимаемое место в теме. Снабдил код подробными комментариями. // IMediator.h Код: #ifndef IMediator_h__ // ColleaguesMediator.h Код: #ifndef ColleaguesMediator_h__ // IColleague.h Код: #ifndef IColleague_h__ // ColleagueA.h Код: #ifndef ColleagueA_h__ // ColleagueB.h Код: #ifndef ColleagueB_h__ // InternalRequest.h Код: #ifndef InternalRequest_h__ // InternalResponse.h Код: #ifndef InternalResponse_h__ // main.cpp Код: #include "ColleaguesMediator.h" Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Igors от Май 22, 2015, 12:22 Цитировать typedef struct tagMSG { Что общего у этого с тем что Вы рассказали? Да почти все. wParam добавьте, он удобен, во многих случаях удается отделаться только им (не создавая структуру в куче всякий раз)HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; ... } Это я к тому что если вещь известна/стандартна, то не лучше ли и в реализации следовать "известным образцам" и (может даже важнее) известным терминам? А так Вы нагородили "нечто" - что не значит "плохо", но во что нужно вникать, прилагать усилия. А это мало кто будет делать - ведь на форум заходят отдохнуть и потрепаться, это нормально. Немедленный ответ или постановка в очередь - это не имеет отношения к паттерну "медиатор". Вообще если связались с очередью - там много чего надо, это отдельный (и большой) разговор, Умейте разделять задачи. Вообще заниматься "общими" вещами страшно приятно, эта болезнь поражает многих. Пока все в рамках абстрактных конструкций и "каких-то примеров" (любых, притянутых за уши) - все получается прекрасно. Но как только конкретная задача - всегда вылазит какая-то загогулина, противная мелочь которая намертво хоронит "красивую конструкцию" Да, и если у Вас так много досуга - я найду чем его заполнить без всякого остатка (в личку) Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Old от Май 22, 2015, 21:39 Вам нужно разобраться, как использовать умные указатели. Так смешивать их с обычными нельзя, объекты могут автоматически разрушаться, а вы будете хранить не валидные указатели.
Регистрировать и разрегистрировать коллег у посредника удобней делать один раз в конструкторе и деструкторе IColleague соответсвенно. Это избавит вас делать это во всех наследниках коллег. Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: AlphaGh0St от Май 24, 2015, 15:52 Спасибо за ответы! А можно подробнее о правильном использовании умных указателей? Может пример кода в качестве образца правильного и не правильного использования?
Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Old от Май 24, 2015, 17:15 Лучше не смешивать умные и обычные указатели в одной программе.
Смотрите: Код Объект o разрушиться в конце блока и если функция runFunc сохраняет указатель на объект в какой-то коллекции, то в дальнейшем коллекция будет хранить указатель на уже разрушенный объект. Через обычные указатели вы не можете контролировать время жизни объектов, поэтому если решили использовать умные указатели, то используйте их везде. Для сильных связей (например, владелец хранит дочерние объекты) используйте shared_ptr, для слабых (например, мы хотим хранить указатель на объект, который не контролируем) - weak_ptr. Хорошо продумывайте деревья владений (какой объект какими владеет (контролирует)), это позволит не создавать циклических сильных связей, при котором объекты могут никогда не разрушиться. Используйте умные указатели в настоящих проектах и вы быстро с ними разберетесь. Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Igors от Май 25, 2015, 07:37 Код: class IColleague 2) Почему m_Mediator - член базового класса? Разве их не может быть несколько? (аналогии напрашиваются, напр подписка на неск форумов). Или наоборот ни одного (см 1)? Если же полагаем что всегда один, то честнее/яснее сделать его синглтоном. Даже если перенести его в конкретные классы коллег, то все равно m_Mediator - "плохой" указатель. Удалить/поменять его просто так нельзя, ведь коллеги его юзают. Правда он хранит список пользующих, по которому можно пробежаться. Ну это еще один наворот 3) Неясно откуда IColleague возьмет указатель на получателя. Если он ему явно известен, то зачем посредник? Как и в жизни, если продавец и покупатель лично знакомы, то часто обходятся без посредников. А вот если "в глаза не видел" - то вряд ли, посредник необходим. 4) Так и не понял схему работы - синхронно или асинхронно? Это должно задаваться явно, напр SendEvent/PostEvent, и определяться отправителем. И кто отвечает за удаление команды? Пока хватит, если интересно - продолжим Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: AlphaGh0St от Май 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) О каком удалении команды Вы говорите? Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Igors от Май 25, 2015, 13:15 Схема работы - синхронная, всё в одном потоке. Хотя, возможно и следовало бы предусмотреть асинхронный вариант. "Синхронно" означает что когда (после вызова) управление вернулось, то все уже свершилось, результат готов (удачный или нет). "Асинхронно" значит что запрос "принят к исполнению" (часто поставлен в очередь) но когда выполнится и придут результаты - хз. Кол-во потоков здесь ни при чем.1) Какая разница между SendEvent и PostEvent? Если имеются в виду методы Qt, то паттерн пишу без использования Qt. Но ведь даже в Qt указывается получатель. Если ли смысл реализовывать аналоги? 2) В каком случае следует делать синхронную работу, а в каких асинхронную? 3) О каком удалении команды Вы говорите? SendEvent и PostEvent - ф-ции WinAPI (синхронный и асинхронный вызовы). В Qt это методы QApplication (называются так же только с маленькой буквы) с примерно тем же ф-ционалом. Разделение методов (Send, Recv) обязательно для асинхронной работы - но совершенно напрасный геморрой для синхронной. Обычно при синхронном вызове команда удаляется отправителем ("я создал - я и удалил"). А при асинхронном всяко, часто самой очередью - ведь до ответа отправителю дело может и не дойти. У Вас не вижу кто вообще удаляет InternalRequest На остальное отвечу позже, надо подумать Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Old от Май 25, 2015, 17:07 Т.е. Вы предлагаете не делать метод отправки обязательным? Нет никаких обязательных методов.Основной посыл этого приема заключается в том, что мы можем организовать взаимодействия множества объектов через один объект посредник и по его правилам. Т.е. если объектов тысячи, нам не нужно в каждом хранить указатели на все остальные объекты - их будет знать посредник. Так же он определяет сам интерфейс и поведение всего взаимодействия. Каждому объекту будет достаточно знать только самого посредника и не обязательно знать все разнообразие и интерфейсы других объектов. Например, у нас есть тысячи объектов разных датчиков, которые могут сообщать друг другу свои состояния. И есть класс посредник, которые знает как обрабатывать их взаимодействие. Код
Что будет происходить в методах Manager::alarm и Manager::setValue это детали реализации. Менеджер может вызывать методы других сенсоров для информирования их о изменении состояния одного из сенсоров, он может сохранять состояния сенсоров в других объектах-хранилищах, передавать эти значения третьим объектам принимающим решения и т.д. Главное, что объекты ничего не знают о других объектах и все общение сводиться через один объект-посредник, который и описывает все разнообразие этого общения. Но тогда возникает вопрос: если будут несколько посредников и коллега пошлёт запрос, как узнать, через какого посредника отправлять этот запрос? Посредников может быть несколько и они могут иметь разный интерфейс. Вам нужно хранить указатели на каждый из посредников в классах коллегах и использовать их методы для взаимодействия.Но, как тогда посредник поймёт, кому передавать запрос? Это все тонкости реализации конкретного посредника. Никто не мешает сделать у посредника метод, который будет подбирать по каким-то критериям объект-коллегу, который может обрабатывать какие-то запросы другого коллеги.Название: Re: [C++] Паттерн Посредник (Mediator) с передачей параметров Отправлено: Igors от Май 26, 2015, 08:57 Обязательный потому, этот метод должен быть судя по диаграмме. :)1) Найти в списке коллегу, который сможет .. Это все возможные варианты - и найдется еще больше. Ваша реализация вполне разумна, но ни на какую общность не претендует. Вообще следование диаграммам не всегда хорошо. Напр утверждение "есть базовый класс "коллега", все должны быть его наследниками". Это не так уж бесспорно. В жизни редко бывает "пример с нуля", обычно уже есть какие-то (часто работающие) классы. Конечно можно и к ним добавить "коллегу" множественным наследованием, но желания так сделать почему-то не возникает. У того коллеги ф-ционала с гулькин нос, и этот ф-ционал типовой (register/unregister/receive). В конкретной задаче я бы думал как обойтись вообще без класса "коллега" используя напр слоты/сигналы, базовые возможности QObject, глянул бы QSignalMapper. А без задачи можно нагородить что угодно - и все пройдет, ведь никаких критериев оценки нет. Но ценность этого невелика. 2) Отправлять запрос всем коллегами из списка, а каждый ... |