Дмитрий Филатов
31 октября 2007 |
|
Задача. | Сделать код более гибким. |
||
Разрабатывая клиентский функционал, постоянно приходится сталкиваться с необходимостью как-то управлять взаимодействием различных компонентов между собой. Под компонентом здесь подразумеваются любые объекты классы, виджеты, контроллеры и т. д. Решение «в лоб» жестко программировать взаимодействие в коде компонентов. Такое решение может работать пока взаимосвязей немного, но даже в этом случае компоненты будут наделены лишним знанием о своем окружении, которым, в общем-то, они не должны обладать. Когда же таких зависимостей становится много, код быстро начинает превращаться в плохоуправляемую кашу, а о повторном его использовании в другом окружении не может быть и речи.
Например, мы запрограммировали красивую фотогалерею с превьюшками и анимацией. По нажатию на превью плавно выезжает полное изображение. Пока все хорошо компонент галереи самодостаточен и ни от чего не зависит. Затем решаем, что в процессе выезда полного изображения должен меняться фон страницы. Добавляем код смены фона в код галереи. Первый тревожный звонок о чрезмерной связанности есть компонент начинает делать то, что не относится к его области ответственности. Назавтра выясняется, что на одних страницах требуется менять фон, на других нет, но нужно скрывать, допустим, навигацию и закрывать форму авторизации, если она была открыта. Если мы начинаем добавлять весь этот код в код галереи, то она становится полностью зависимой от своего окружения, которое еще и может меняться. А код галереи при этом становится похожим на сборную солянку и выполняет слишком много лишних действий. Разобраться в нем вообще становится практически невозможно.
Как же быть? Посмотрим в сторону паттернов. Нам подходят два «Наблюдатель» (Observer) и «Посредник» (Mediator). Терминология взята из книги GoF.
Данный паттерн предполагает две группы участников: «Наблюдатели» и «Наблюдаемые». Первые подписываются на события вторых, вторые при необходимости оповещают первых.
Немного доработав для нашей задачи, получаем следующий интерфейс Observable:
|
|
Где sEventType вид события, mObserver наблюдатель, который может быть как объектом, так и callback-функцией.
Кстати, в самом javascriptе используется подобная модель с callback-функцией для добавления обработчиков событий на DOM-элементы.
Теперь каждый компонент, реализующий интерфейс Observable, может сам определять типы событий, которые в нем происходят. В случае с галереей, например, это могли бы быть такие виды событий: onInit, onStartOpen, onEndOpen, onStartClose, onEndClose. При необходимости компонент вызывает метод notify с указанием нужного типа события, при этом происходит оповещение всех наблюдателей, подписавшихся на данный вид события.
Как же одни компоненты будут подписываться на события других компонентов, при этом ничего не зная о них?
Чтобы компоненты остались независимыми, в качестве наблюдателей лучше использовать не сами компоненты (так как в этом случае они опять же будут наделены лишним знанием), а объекты-посредники, которые уже обладают знанием о текущем окружении, его компонентах и их взаимосвязях. При таком подходе мы сможем менять посредников в зависимости от окружения, оставляя чистым код самих компонентов и сильно повышая шансы его повторного использования.
Код использует некоторые возможности для работы с массивами из библиотеки Common.js.
|
|
Чтобы воспользоваться данным подходом, необходимо Observable сделать базовым классом вашего класса.
При использовании Common.js это будет выглядеть примерно так:
|
|
Если по каким-то причинам использовать наследование не получится, можно воспользоваться делегированием:
|
|
Подписывание на события объекта MyComponent выглядит так:
|
|
© 19952024 Студия Артемия Лебедева
|