Опубликован: 04.04.2012 | Доступ: свободный | Студентов: 1989 / 61 | Оценка: 4.60 / 4.40 | Длительность: 13:49:00
Лекция 9:

Образцы проектирования

< Лекция 8 || Лекция 9: 12345 || Лекция 10 >

Реализация типа события

Вернемся к рассмотрению сути — знакомству с реализациейEVENT_TYPE. Она подобна приводимой выше реализации PUBLISHER . Закрытый компонент subscribers хранит список подписчиков. Его сигнатура теперь такова:

subscribers: LINKED_LIST [PROCEDURE [ANY, ARGUMENTS]]

Здесь, как и прежде,LINKED_LIST — наивная структура, но вполне достаточная для нашего обсуждения (лучшую структуру можно увидеть в тексте фактического классаEVENT_TYPE из Event-библиотеки; можно также выполнить упражнение в конце лекции).

Элементы, хранимые в списке, больше не являются подписчиками, понятие, в котором теперь архитектура решения не нуждается, — это просто агенты. Тип каждого такого агента, PROCEDURE [ANY, ARGUMENTS] указывает, что агент представляет процедуру с аргументами кортежного типа ARGUMENTS, как это определено в классе. Это значительно улучшает безопасность типов решения, в сравнении с тем, что мы видели ранее: несоответствия будут захвачены во время компиляции как плохие аргументы subscribe .

Метод subscribe (в наивной реализации) может выглядеть так:

subscribe (action: PROCEDURE [ANY, ARGUMENTS])
    - Зарегистрировать действие, которое должно быть выполнено для
    - событий этого типа.
  do
    subscribers.extend (action)
  ensure
    present: subscribers.has (action)
  end

Использование ARGUMENTS — второго родового параметра класса в типе PROCEDURE — гарантирует, что все процедуры будут отвергнуты, если они не имеют аргументов нужного типа.

Для публикации события мы проходим по списку и вызываем соответствующих агентов. Фактически, это тот же код, что и в классе PUBLISHER в образце "Наблюдатель", хотя args теперь имеют более подходящий тип — ARGUMENTS :

publish (args: ARGUMENTS)
      — Публиковать событие подписчикам.
  do
    — Включить событие этого типа.
    from subscribers.start until subscribers.after loop
      subscribers.item.call (args)
      subscribers.forth
    end
end

Любой аргумент при вызове метода агента должен быть кортежем, поскольку ARGUMENTS ограничены типом кортежа.

Только что описанное решение лежит в основе библиотек Event и EiffelVision GUI. Оно широко используется для графических приложений, как небольших, так и сложных, включая само окружение EiffelStudio.

Класс включает еще несколько деталей, которые стоит внимательно рассмотреть.

Время чтения программ!

Типы событий

Познакомьтесь с текстом библиотечного классаEVENT_TYPE и убедитесь, что вы все в нем понимаете.

Дисциплина подписчиков

Если применять любой из приемов этой лекции — от несовершенного образца "Наблюдатель"публиковать-подписываться/codeuot; до механизма, основанного на агентах, — то следует уделить внимание проблемам производительности, которые могут приводить к потенциально разрушительным потерям памяти. Избежать их достаточно просто, если корректно определять поведение подписчиков.

Почувствуй методологию

Не забывайте вовремя отменять подписку

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

Чем вызвано это правило? Проблемой в использовании памяти. Из реализации subscribe, как для версии PUBLISHER в образце "Наблюдатель", так и для версии из классаEVENT_TYPE, следует, что издатель записывает в список подписчиков ссылку на объект подписчика. В приложениях GUI издатель принадлежит облику, а подписчик — модели. Поэтому объект облика сохраняет ссылку на объект модели, который, в свою очередь, может содержать другие ссылки на многие объекты модели (например, самолеты, полеты, расписание и так далее в системе управления полетами).

Но тогда становится невозможным, если только сами объекты облика становятся бесполезными, избавиться от такого модельного объекта, даже если он вычислению уже не нужен. В современных средах разработки сборщик мусора никогда не будет собирать объекты, на которые ссылаются другие объекты. Если же освобождение памяти выполняется вручную (как в С и С++ окружении), то ситуация становится еще хуже. В любом случае, появляется источник "потерь памяти": выполнение не справляется с возвращением неиспользуемых объектов, объем занимаемой памяти продолжает расти.

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

Методологические правила никогда не бывают столь же эффективными, как инструментарий и архитектура, гарантирующая желаемые цели. В данном случае нет очевидного способа автоматической ликвидации ненужной подписки — остается полагаться на методологические советы.

Когда вы подписываетесь на агента и хотите позже иметь возможность отмены подписки, следует использовать переменную, представляющую агента:

handler:=agent p
left_click.subscribe (handler)
    ... — Тогда, когда пришло время отмены подписки:
left_click.unsubscribe ( handler)

Подписка через переменную, задающую агента, вместо его непосредственного использования, как в предыдущих примерах left_click.subscribe (agent p), гарантирует, что отмена подписки применяется к тому же самому объекту (в отличие от left_click.unsubscribe (agent p), который мог быть применен к новому объекту). Если вы уже подписали данный обработчик более одного раза к данному типу события, отмена (использующая remove_all ) удалит все такие подписки.

Архитектура ПО. Уроки

Проекты, рассмотренные в этой лекции, позволяют сделать некоторые общие выводы об архитектуре ПО.

Выбирайте правильные абстракции

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

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

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

РассматриваяEVENT_TYPE как ключевую концепцию, приходим к основному классу финального проектирования. Тем самым удается избежать наследования от родителей специального вида для классов подписчиков и издательских классов. Издатель теперь — это простой элемент ПО, использующий publish метод для некоторого типа события, а подписчик — элемент, применяющий метод subscribe .

< Лекция 8 || Лекция 9: 12345 || Лекция 10 >
Надежда Александрова
Надежда Александрова

Уточните пожалуйста, какие документы для этого необходимо предоставить с моей стороны. Курс "Объектно-ориентированное программирование и программная инженения".