Практика применения паттернов проектирования
От: Беркович Вадим, Чудин Андрей http://shop.biblio-globus.ru/cpr/
Дата: 23.04.03 08:38
Оценка: 285 (11)
Статья:
Практика применения паттернов проектирования
Автор(ы): Беркович Вадим, Чудин Андрей
Дата: 09.04.2003
Практически во всех проектах можно встретить те или иные паттерны проектирования. Но далеко не часто они обозначены разработчиками. Проект, в котором явно обозначены все использованные паттерны, удобнее для понимания и более управляем. Можно сказать, что описание проекта в терминах паттернов добавляет новые метаданные о проекте. Если мы считаем, что данный класс реализует паттерн "итератор", мы сразу получаем представление об его интерфейсе и роли. Если же изначально весь проект реализован с использованием паттернов, то управление проектом упрощается. Обобщение удачных решений конкретных задач в паттерны и использование их в последующих проектах существенно ускоряет процесс разработки. А код становится более понятным и элегантным, и им можно будет воспользоваться повторно.


Авторы:
Беркович Вадим, Чудин Андрей

Аннотация:
Практически во всех проектах можно встретить те или иные паттерны проектирования. Но далеко не часто они обозначены разработчиками. Проект, в котором явно обозначены все использованные паттерны, удобнее для понимания и более управляем. Можно сказать, что описание проекта в терминах паттернов добавляет новые метаданные о проекте. Если мы читаем, что данный класс реализует паттерн "итератор", мы сразу получаем представление об его интерфейсе и роли. Если же изначально весь проект реализован с использованием паттернов, то управление проектом упрощается. Обобщение удачных решений конкретных задач в паттерны и использование их в последующих проектах существенно ускоряет процесс разработки. А код становится более понятным и элегантным, и им можно будет воспользоваться повторно.
Не все мне понятно
От: IDm Россия  
Дата: 03.05.03 16:56
Оценка: 1 (1) +1
Я конечно в паттернах шарю не очень, но ИМХО TModuleFactory — есть паттерн Фабричный Метод (Factory Method). По крайней мере нет абстрактной фабрики, нет НЕСКОЛЬКИХ конкретных фабрик (а только одна).
по поводу медиатора.
От: Saruman Россия none
Дата: 28.04.03 06:56
Оценка:
Вcе просто замечательно, но... Не стоит обольщаться господа :) В реальных проектах бывает и по 500 форм и порой действительно невозможно предсказать, как будет взаимодействовать данная форма с другими. Медиатор тут как раз в самый раз. Но ! В том же проекте большинство форм должно создаваться динамически, ибо создавать все 500 форм на этапе старта — это огромный расход памяти и времени. А как организовать работу медиатора, если тот кому назначается команда еще не создан — не понятно... :(
Типа того что как бы...
Так а в чем паттерны то?
От: The Lex Украина  
Дата: 25.04.03 08:00
Оценка:
Где они? В чем соль?
Голь на выдумку хитра, однако...
Рисунки не помещаются на страницу
От: YuriS Германия www.yuris.de
Дата: 24.04.03 08:38
Оценка:
Рисунки 1,2,3,5,9 в версии 1.0 не помещаются по ширине на печатную страницу, в связи с этим не возможно правильно распечатать эту статью. :(
Re: по поводу медиатора.
От: Good Украина  
Дата: 30.04.03 07:08
Оценка: 14 (2)
Дык решается проблема с 500 формами, создаешь не сами формы, а их какие-то оболочки, в которых, допустим, есть доступ к указателю на саму форму, а также методы для инициализации медиатора и т.д. и доступ к этому указателю организовываешь по принципу "одиночки", при первом доступе к нему создашь, собственно, форму, а при всех последующих — вернешь просто указатель на нее. Такми образом механизм с медиаторами проинициализируется, а формы создадуться в тот момент когда они понадобятся. Я думаю, что много времени на создание 500 таких оболочек не уйдет. Покритикуй ;) Может я где-то и не прав, это то что мне сразу в голову пришло.
Re: Рисунки не помещаются на страницу
От: emag  
Дата: 24.04.03 10:12
Оценка:
А ты ее в landscape распечатывай :)
Re: Рисунки не помещаются на страницу
От: YuriS Германия www.yuris.de
Дата: 24.04.03 13:00
Оценка:
Тогда по высоте не влеэут :)
Re: Практика применения паттернов проектирования
От: aston Россия  
Дата: 23.07.03 16:14
Оценка: +2
G>Затем в цикле создаются модули, посредством вызова функции CreateModule, в которой >один из параметров имеет тип модуля (TModuleType), а второй – это ссылка на Медиатор.

IMHO, не самый удачный вариант — создавать модули в стартапе приложения (даже если ГУИ в них создается динамически). Тем более модули из приведенного примера, где лежат StoredProc-ы, Query и прочие Table'ы. По моему, выгодней хранить описания модулей в конфиге (файл, БД), а уж читая описания — искать модуль, создавать (активизировать), удалять (деактивизировать, помещать в пул), а сами модули помещать в отдельные DLL.

G>Модуль-отправитель создает объект-команду для модуля-получателя и вызывает метод >медиатора SendMessage(TCustomCommand). Медиатор последовательно просматривает все >зарегистрированные в нем модули и вызывает их команду ExecCommand(TCustomCommand). >Вызываемая процедура Execute поверяет тип команды и или выполняет ее, или отвергает. >Команды выполняются в синхронном режиме. Данный подход напоминает паттерн >Publisher-Subscriber. На рис. 8 показана структура классов команд и их взаимосвязь с >Медиатором.


Принудительный broadcasting команды, когда она последовательно будет обработана всеми модулями, которые имеют ее реализайцию тоже не есть гуд.
Правильней бы было все же позволить клиенту явно указать модуль, которому адресуется команда, а если уж не указал, тогда broadcasting. Реальная ситуация из приведенного в статье примера. Есть несколько модулей с одниковыми команами. В случае, как предлагает автор, количество команд, которые необходимо описать, составляет "кол-во модулей"*"кол-во команд". А можно было бы описать только неповторяющиеся команды и адресовать их определенным модулям.

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

>
>Добавить соответствующее значение в перечисление TModuleType;
>Наследовать модуль от TAppModule и перегрузить операции GetName, GetOperations, >Execute;
>Добавить в фабрику классов TModuleFactory код создания модуля.

А вот с этим подходом категорически не согласен. Для того, чтобы при малейшем изменении функциональности перекомпилировать все приложение?!!!. Как говорил Голубицкий: "Полная НАРВАСАДАТА — Камасутра отдыхает".

Спасибо за внимание.
... << RSDN@Home 1.0 beta 4 >>
Re[2]: Практика применения паттернов проектирования
От: batyi  
Дата: 12.08.03 10:05
Оценка:
aston, Вы писали:

A>Принудительный broadcasting команды, когда она последовательно будет обработана всеми модулями, которые имеют ее реализайцию тоже не есть гуд. Правильней бы было все же позволить клиенту явно указать модуль, которому адресуется команда, а если уж не указал, тогда broadcasting.


Соглашусь с этим. Что меня не радует в архитектуре Windows так это наличие общей очереди сообщений на весь поток. Поясню: вам нужно в ответ на некоторое конкретное сообщение выполнить конкретную процедуру. Для этого во многих случаях приходится пробираться сквозь бесконечные switch пока сообщение увидит тот, кому оно действительно нужно. В данном случае архитектура стремится к тому же. В объектно ориентированном программировании часто рекомендуется использовать понятие интерфейса и виртуальной функции. А именно: каждому типу или набору сообщений, соответствует свой тип интерфейса (в C++ это реализуется множественным наследованием, в Java объявлением поддерживаемого интерфейса). Сторона, отапрвляющая сообщение проверяет, поддерживает ли цель подходящий интерфейс, и если да, то может вызвать его метод. Здесь ещё подойдёт паттерн Event (который позволяет динамически соединять источник события и его получатель).

>>Добавить соответствующее значение в перечисление TModuleType;

>>Наследовать модуль от TAppModule и перегрузить операции GetName, GetOperations, >Execute;
>>Добавить в фабрику классов TModuleFactory код создания модуля.
A>А вот с этим подходом категорически не согласен. Для того, чтобы при малейшем изменении функциональности перекомпилировать все приложение?!!!.

Для небольщих проектов это может быть и приемлемо, я вот для средних и больших лучше рассмотреть возможность использования каких-нибудь компонентных технологий (COM, XPCOM) или же самим сделать интерфейс для плагинов.
Re: Практика применения паттернов проектирования
От: adb Россия  
Дата: 05.09.03 09:57
Оценка: +1
Когда-то сам дошел до подобного подхода. Единственное, что сущностью у меня были не модули, а отдельные команды (сначала на тулбаре, а потом и просто логика). К ним же еще добавлялось свойство enable (для дизайблинга кнопок тулбара и пунктов меню).

Небольшое замечание по реализации. Вместо бродкаста IMHO лучше использовать subscribe. Т.е. модуль подписывается в медиаторе на некоторый набор команд. Команды эти храняться например в map'е по ключю "команда". В этом случае, достаточно по команде выявить подписчика и перенаправить команду ему. По перфомансу это лучше чем обходить всех всегда. Если у команды несколько обработчиков, то можно использовать multimap.

P.S. Эту статью я бы все таки в форум Проектирование отправил.
Re: Практика применения паттернов проектирования
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.09.03 11:00
Оценка: +1
Здравствуйте, Беркович Вадим, Чудин Андрей, Вы писали:

Статья неплохая. К недостаткам я бы отнес крайне слабое использование средств поддержки паттернов, встроенных в язык.
Так, например, то, что в статье называется "абстрактной фабрикой", выглядит совершенно исскусственным. Использование отдельного перечисления для типа создаваемого модуля ничем не мотивировано — с тем же успехом можно передавать в фабрику сразу идентификатор класса. Подумав еще немного, можно понять, что в таком случае код фабричного метода выродится в вызов Application.CreateForm (а в более общем случае — в вызов виртуального конструктора). Что и требовалось доказать — все, что надо, уже сделано за нас. Собственно, приведенный код представляет собой кальку с кода по созданию форм, автоматически генерируемого средой Delphi в теле программы.
Для получения сколь-нибудь полезного результата необходимо оборудовать ядро приложения возможностью динамической регистрации модулей без перекомпиляции существущего кода, благо все средства для этого в языке уже есть.

Использование паттерна Медиатор в данном контексте также несколько рискованно — при росте количества модулей эффективность будет падать. Кроме того, нет никакой гарантии единственности получателя, а это источник потенциально трудноуловимых ошибок.
При этом каждый модуль точно знает, какие команды он "хочет" обрабатывать, что позволяет реализовать намного более эффективный механизм подписки на команды.
Кстати, для его реализации в Object Pascal введен специальный тип — указатель на метод объекта. Он позволяет выполнить нужный вызов без лишних накладных расходов.
Единственное, для чего он плохо подходит — это для поддержки множественных получателей. С другой стороны, семантика любой команды, как правило, определяет возмоджность либо невозможность существования множественных получателей.
Т.е. publisher/subscriber значительно эффективнее реализует требуемую функциональность, чем медиатор, при этом потери гибкости не происходит.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Практика применения паттернов проектирования
От: Аноним  
Дата: 22.10.03 08:59
Оценка:
Насколько я понял из статьи (и из парочки других статей по ООП), при "правильном" проектировании дельфийской программы придётся отказаться от использования DB-Aware-компонентов и писать взаимодействие с БД вручную. Сейчас я беру TDBGrid, указываю ему: из какого источника показывать/изменять данные и -- всё. Но при этом уровни представления, логики и взаимодействия с БД мешаются в кучу. А при "правильном" подходе придётся взять TGrid и -- прощай все созданные Борландом лёгкость и удобство?
Re: по поводу медиатора.
От: Аноним  
Дата: 21.06.04 14:30
Оценка:
Здравствуйте, Saruman, Вы писали:

S> А как организовать работу медиатора, если тот кому назначается команда еще не создан — не понятно...


По паттерну Publisher-Subscriber. При увеличении числа сущностей в проекте широковещательная рассылка команд становится нерациональной. В этом случае модули проекта (если придерживатся терминологии обсуждаемой статьи) должны подписываться на интересующие их классы сообщений. К примеру, модуль, инкапсулирующий сведения о клиентах, подписывается на TCustomClientCommand, а модуль, обеспечивающий протоколирование работы программы — на базовый TCustomCommand. Каждый модуль может "оформлять" несколько подписок. При отправке команды Медиатор рассылает ее только заинтересованным модулям. И всем Хорошо.
---
Чудин Андрей
Re[2]: по поводу медиатора.
От: wildwind Россия  
Дата: 21.06.04 17:02
Оценка:
Однако имеем противоречие.
С одной стороны,чтобы модуль мог подписаться на команду, его нужно как минимум загрузить.
С другой стороны, хотим отложить загрузку модуля до первого вызова команды, которую он (может быть) исполняет.
Значит есть потребность в неком слое метаданных, которыми владеет кто-то один. То есть все-таки Медиатор.
А еще представьте что мы захотим наложить на все это контроль доступа...
Re[3]: Зачем?
От: delphinchik Россия  
Дата: 28.12.04 07:12
Оценка:
Статья мне понравилась. Вот только зарывшись поглубже возникли некоторые вопросы. Вот один из них (для тех, кто видел исходники):
Файл проекта:
program appTemplate;

{
 Шаблон проекта;

 приложение к статье "Практика применения паттернов проектирования"
 - ж. RSDN Magazine #3
}



uses
  Forms,
  uAppConsoleForm in 'uAppConsoleForm.pas' {AppConsoleForm},
  uAppConsole in 'uAppConsole.pas' {AppConsole: TDataModule},
  uModuleFactory in 'uModuleFactory.pas',
  uCommands in 'uCommands.pas',
  uCommonModule in 'uCommonModule.pas' {dmCommonModule: TDataModule},
  globalConst in 'globalConst.pas',
  uAppModule in 'uAppModule.pas' {AppModule: TDataModule},
  uConverterFactory in 'uConverterFactory.pas',
  uCustomEditor in 'uCustomEditor.pas' {fCustomEditor},
  uCustomBrowser in 'uCustomBrowser.pas' {fCustomBrowser},
  uClientsAppModule in 'uClientsAppModule.pas',
  htmlhelp in 'htmlhelp\htmlhelp.pas';

{$R *.RES}


begin
  Application.Initialize;
  Randomize;

  Application.CreateForm(TdmCommonModule, dmCommonModule);
  Application.CreateForm(TAppModule, AppModule);
  Application.CreateForm(TAppConsole, AppConsole);
  Application.Run;

end.

{ История изменений ведется в файле _history.inc }


Интересует смысл выделенной строки. Зачем создавать объект абстрактного класса? Тем более, что он нигде далее в программе не использутся и она сможет свободно работать и без объекта AppModule?
Re: Практика применения паттернов проектирования
От: Аноним  
Дата: 10.01.05 19:44
Оценка:
Здравствуйте, Беркович Вадим, Чудин Андрей

хотелось взглянуть на скомпилированный проект но
к сожалению в статье даны ссылки не на все библиотеки необходимые для компиляции проекта

к сожалению
это частый случай когда делфисты забывают что то из понакиданных на формы компонент
Re[2]: Практика применения паттернов проектирования
От: Dimonka Верблюд  
Дата: 10.01.05 19:59
Оценка: -1
Здравствуйте, Аноним, Вы писали:

А>хотелось взглянуть на скомпилированный проект но

А>к сожалению в статье даны ссылки не на все библиотеки необходимые для компиляции проекта

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

А>к сожалению

А>это частый случай когда делфисты забывают что то из понакиданных на формы компонент

Не будьте таким строгим
Re: Практика применения паттернов проектирования
От: Sergey__ Россия  
Дата: 14.01.05 07:34
Оценка:
Здравствуйте, Беркович Вадим, Чудин Андрей

в примере — используется библиотека RX
т.к. RX не является стандартной , переживает не самые лучшие времена и иемеет разные версии (смотря кто адаптирует под свежий делфи)
то может имеет смысл дать в статье ссылку (GlobusLib) и на RX, используемую Вами ?
Sergey
Re[2]: Практика применения паттернов проектирования
От: delphinchik Россия  
Дата: 28.01.05 14:12
Оценка:
Вопрос такой: По логике программы интерфейс формируется в одном месте, в модуле uAppConsoleForm. Насколько я понимаю и в дальнейшем, при работе программы нужно придерживаться этого правила. А как быть в том случае, когда какому то из модулей главная форма отправляет команду, выполнение которой влечет за собой измнение отображения формы. Ведь модули не должны участвовать в создании интерфейса.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.