Proxies
Вообще говоря, паттерн Прокси (Proxy, далее Прокси) используется для обеспечения хранения любого объекта в целях контроля доступа к нему. В приложениях, основанных на PureMVC, класс Proxy используется специально для управления частями Модели Данных приложения.
Прокси могут управлять доступом к созданным локально структурам данных произвольной сложности. Они называются Объектами Данных Прокси (Proxy’s Data Object).
В этом случае, идиома для взаимодействия с Прокси связана с синхронной установкой (setting) и получением (getting) данных. Объекты Данных могут подвергаться полному или частичному обновлению свойств, или же может меняться ссылка на сам Объект Данных. Когда выполняются методы для обновления данных, Прокси может отправлять Оповещение в остальную часть системы, что его данные изменились.
Прокси Удаленного Сервиса (Remote Proxy, далее Прокси Удаленного Сервиса) используется для инкапсуляции взаимодействия с удаленными сервисами, для сохранения или получения данных. Прокси может хранить объект, который взаимодействует с удаленным сервисом, а также контролировать доступ к данным, переданным и полученным от этого сервиса.
Таким образом, можно просто устанавливать данные или же вызывать методы Прокси и дожидаться асинхронного Оповещения, отправляемого Прокси когда данные от удаленного сервиса получены.
Обязанности конкретного Прокси
Конкретные Прокси позволяют нам инкапсулировать фрагменты модели данных, независимо откуда они получены и их типа, а так же необходимы для управления Объектами Данных и организации доступа приложения к ним.
Реализация класса Прокси, который входит в PureMVC, является простым контейнером для Объектов Данных, и может быть зарегистрирована в Модели.
Чтобы полностью использовать возможности в данной форме, мы, обычно, создаем подкласс класса Proxy и добавляем функциональность, характерную для конкретного Прокси.
Общие варианты использования Прокси включают в себя:
- Прокси Удаленного Сервиса - данные, управляемые конкретным Прокси, находится на удаленном сервере, и могут быть доступны через удаленный сервис.
- Прокси и Делегат (Proxy and Delegate) - доступ к объекту, обеспечивающему удаленный доступ, должен быть распределен между несколькими Прокси. Класс Делегат поддерживает объект удаленного доступа и контролирует доступ к нему, обеспечивая передачу ответов тем, кто послал запрос.
- Защищенный Прокси (Protection Proxy) - используется когда игроки системы должны иметь разные права доступа к Объекту Данных.
- Виртуальный Прокси - создает Объекты Данных по запросу.
- Умный Прокси - загружает объекты данных в память при первом доступе, осуществляет ведение учета ссылок, позволяет блокировать объект, чтобы другой объект не смог изменить его.
Неявное приведение типа объекта данных
Реализация базового класса Proxy, которая поставляется с PureMVC принимает имя Прокси и основной Объект Данных в качестве аргумента конструктора. Вы можете динамически устанавливать Объект Данных Прокси после того, как он создан, вызывая метод setData.
Как и в случае с Медиатором и его Компонентом Представления, вы будете часто приводить Объект Данных к фактическому типу, для того, чтобы получить доступ к свойствам и методам, которые он предоставляет; это утомительная и однообразная практика, но следование идиомам позволяют открывать больше о реализации Объекта Данных, чем это может потребоваться.
Кроме того, поскольку Объект Данных обычно имеет сложную структуру, часто необходимо иметь под рукой ссылки на несколько частей структуры, в дополнении к приведенной к типу ссылки на всю структуру.
В языке JavaScript функции называемые геттерами (getters) и сеттерами (setter) оказываются очень полезными в решении приведения типа и устранении проблем неуместного применения типов.
Полезная практика - использовать в конкретном Прокси getter, который возвращает Объект Данных фактического типа и имеет осмысленное название.
Дополнительно, вы можете указать несколько геттеров, возвращающих определенные части Объекта Данных.
Например:
@property get searchResultAC(): ArrayCollection {
return this.getData();
}
@method resultEntry(index: number): SearchResultVO {
return this.searchResultAC[index];
}
В каком-то Медиаторе вам пришлось бы делать так:
const data: ArrayCollection = searchProxy.getData();
const item: SearchResultVO = data[1];
Однако, используя практику, описанную выше, можно сделать так:
const item: SearchResultVO = searchProxy.resultEntry(1);
Запрет на привязку к медиаторам
У Прокси не запрашивают список интересующих его Оповещений как у Медиатора, да он и не получает Оповещений, потому что он не должен заботиться о состоянии Вида. Вместо этого Прокси предоставляет методы и свойства, позволяющие другим участникам манипулировать им.
Конкретный Прокси не должен извлекать и использовать Медиаторы для того, чтобы информировать систему об изменении своего Объекта Данных.
Вместо этого Прокси должен отправлять Оповещения, которые получат Команды или Медиаторы. От Прокси не должно зависеть, как эти Оповещения повлияют на систему.
В связи с тем, что уровень Модели Данных не содержит каких-либо знаний о системе реализации, уровни Представления и Контроллера могут быть отрефакторены без вмешательства в уровень Модели Данных.
Обратное не совсем верно. Очень трудно изменить уровень Модели Данных, не изменяя уровень Представления и, скорее всего, уровень Контроллера. В конце концов, эти уровни существуют только чтобы позволить пользователю взаимодействовать с уровнем Модели Данных.
Инкапсуляция предметной области в прокси
Изменения на уровне Модели Данных почти всегда приводит к некоторому рефакторингу уровней Контроллера и Представления.
Мы увеличили разделение между уровнем Модели Данных и общих интересов уровней Представления и Контроллера путем максимального перемещения предметной области в Прокси.
Прокси может использоваться не только для контроля доступа к данным, но и выполнять операции над данными, которые могут быть необходимы для поддержания некоторого их валидного состояния.
Например, расчет налога с продаж является функцией предметной области и поэтому она должна находится в Прокси, а не в Медиаторе или Команде.
Несмотря на то, что эта функция может быть реализована в любом из этих мест, размещение её в Прокси не только логично, но и облегчает другие уровни и упрощает рефакторинг.
Медиатор может получить Прокси; вызвать функцию вычисления налога с продаж, и возможно, поместить результат в какую-либо форму. Но размещение фактического расчета в Медиаторе реализует в нем, с точки зрения уровня, Предметную Область. Вычислений налога является правилом относящемся к Предметной Области. Представлению оно может быть известно как свойство Предметной Области, доступное, если соответствующий Объект Данных присутствует.
Представьте, что вы в настоящее время работаете над RIA приложением для запуска в размерах рабочего стола персонального компьютера. Новая версия должна запускаться на КПК с соответствующим разрешением с сокращением сценариев использования, но по-прежнему использовать полную Модель Данных существующего приложения.
При правильном разделении интересов, мы можем использовать уровень Модели Данных во всей ее полноте и просто подгонять новые уровни Представления и Контролера к нему.
Размещение фактического расчета налога с продаж в Медиаторе может показаться эффективным и простым в момент реализации; например, вы только что получили данные из формы, и вы хотите рассчитать налог с продаж, и отправить его в Модель уже вычисленным.
Однако в каждой версии своего приложения вам теперь придется дублировать ваши усилия или копировать код вычисления налога с продаж в новый, совершенно другой уровень Представления, хотя эта логика могла бы появляться автоматически, как часть вашей Модели Данных.
Взаимодействие с Прокси Удаленного Сервиса
Прокси Удаленного Сервиса - это обычный Прокси, который получает свои Объекты Данных из удаленного местоположения. Это значит, что мы взаимодействуем с ним в асинхронном режиме.
Каким образом Прокси получает данные - зависят от платформы клиента, реализации удаленного взаимодействия, а также предпочтений разработчиков. Mы можем использовать XMLHttpRequest, WebSocket, fetch(), ... для осуществления запросов из Прокси.
В зависимости от потребностей, удаленный Прокси может направлять запросы динамически, в ответ на вызов сеттеров или методов; или же может делать единственный запрос во время создания и обеспечивать доступ к данным впоследствии.
Есть ряд оптимизаций, которые могут быть применены в Прокси для повышения эффективности взаимодействия с удаленной службой.
Прокси может кешировать данные, и, таким образом, сократить количество запросов в сеть, или же отсылать обновления только тех частей структуры данных, которые были изменены, уменьшая сетевой трафик.
Если запрос динамически вызывается на Прокси Удаленного Сервиса другим игроком в системе, Прокси необходимо отправить Оповещение, когда будет получен ответ.
Заинтересованным в получении Оповещения может быть или не быть тот же игрок, что и инициировал запрос.
Например, для осуществления процесса поиска, происходящего на удаленном сервере и отображения результатов, возможна следующая последовательность действия:
- Представление инициирует поиск вызовом события (Event).
- Его Медиатор получает соответствующий удаленный прокси и устанавливает свойство "критерии поиска".
- Свойство "критерий поиска" в Прокси - это на самом деле неявный сеттер, который сохраняет значение и инициирует поиск запросом через внутренний fetch().
- Затем Прокси отправляет Оповещение, информирующее об успехе поиска и содержащее ссылку на свой Объект Данных в качестве тела Оповещения.
- Другой Медиатор, выразивший заинтересованность в этом Оповещении, и, соответственно, принимающий его, устанавливает тело Оповещения как dataProvider своего Компонента Представления.
Или же рассмотрим LoginProxy, который содержит LoginVO (Value Object; простой класс-контейнер данных).
LoginProxy содержит методы для установления полномочий, входа и выхода пользователя, и получения метки авторизации, которая будет включена в последующие удаленные вызовы для опознания пользователя в данной конкретной схеме аутентификации.
Подробные примеры будут в следующих разделах.