PureMVC. Краткий обзор
pureMVC – это библиотека от Cliff Hall, написаная на разных языках (Ruby, Python, PHP и др.) для поддрержки шаблона Model View Controller. Существует так же версия для ActionScript. Скачать ее можно здесь. Структура папок
Структура pureMVC приложения очень проста, она состоит их трех папок controller, model, view. Выглядит это обычно так:
| |- controller
| | |- Файлы контроллера
| | ...
| |- model
| | |- Файлы и папки модели
| | ...
| |- view
| | |- components
| | | |-Визуальные компоненты вашего приложения (mxml)
| | | ...
| | |- ApplicationMediator.as ( и другие посредники компонентов )
| | ...
| |- ApplicationFacade.as
|- Ваше приложение.mxml
- controller – используется для хранения классов Команд.
- model – здесь хранятся Прокси и другие классы связанные с моделью данных. Например: классы Value Object или разные утилиты для работы с данными, константы.
- view – здесь хранятся Посредники, по одному на каждый компонент, компоненты хранятся, соответственно в подпапке “components”
- ApplicationFacade.as – Фасад
- Ваше приложение.mxml – mxml файл вашего приложения
Фасад
ApplicationFacade– класс который обеспечивает совместную работу трех частей шаблона. Часто Фасад часто содержит статические строковые константы, которые являются уникальными именами Уведомлений(Notification). Они используются для идентификации Уведомлений и передаются как первый параметр в функцию sendNotification().
Фасад выглядит примерно так:
import org.puremvc.as3.patterns.facade.Facade;
import com.undr.myApplication.controller.*;
public class ApplicationFacade extends Facade implements IFacade {
public static const STARTUP_ACTION:String = "startupAction";
public static function getInstance():ApplicationFacade {
if ( instance == null ) {
instance = new ApplicationFacade();
}
return instance as ApplicationFacade;
}
public function startup( app:myApplication ):void {
sendNotification( STARTUP_ACTION, app );
}
override protected function initializeController():void {
super.initializeController();
registerCommand( STARTUP_ACTION, StartupCommand );
}
}
getInstance()– стандартный интерфейс Одиночкиstartup()– инициализация фасада, запускается командаSTARTUP_ACTION. Этот метод срабатывает при старте приложения (событиеcreationComplete) и принимает, в качестве параметра, объект…initializeController()– инициализация контроллера. В этом методе регистрируются Уведомления как команды.
При отправке этого Уведомления вызывается метод execute() зарегистрированного объекта комманды. В данном примере при отправки Уведомления STARTUP_ACTION произойдет вызов метода execute() объекта StartupCommand
Фасад содержит ссылки на все прокси-объекты модели и все объекты-посредники. Создаются и регистрируются они обычно при старте в StartupCommand
var someProxy:SomeProxy = new SomeProxy();
// Тут можно произвести какие-нибудь операции над SomeProxy
facade.registerProxy( someProxy );
facade.registerMediator( new ApplicationMediator( app ) );
facade.registerMediator( new SomeComponentMediator( app.someComponent) );
facade– это объект фасада.facade.registerProxy()-регистрация прокси. Более подробно о создании прокси см. раздел “Proxy”facade.registerMediator()– регистрация посредника. Более подробно о создании прокси см. раздел “Посредники”
Сообщения (Notification)
Посредники, команды и посредники общаются друг с другом посредством Уведомлений.
- Прокси могут отсылать Уведомления но не могут их на них подписываться.
- Команды могут отсылать Уведомления и исполняться по отправке зарегистрированных для этого в Фасаде Уведомлений.
- Посредники могут отсылать и подписываться на Уведомления
Уведомления посылаются функцией sendNotification(), она принимает три параметра: первый – имя Уведомления , второй – дополнительный параметр, который вы хотите передать объекту подписанному на получение этого Уведомления и третий – это тип Уведомления .
В качестве хранилища имен Уведомлений используется ApplicationFacade так как он виден во всех классах pureMVC. Но в это качестве этого можно использовать любой отдельный класс, тем более если имена Уведомлений может потребоваться использовать других приложениях.
Типичный пример отправки сообщения :
Контроллер
Команды лежат в папке controller. Делятся на два вида: SimpleCommand и MacroCommand. MacroCommand выполняет несколько SimpleCommand. Все они должны быть закреплены в Фасаде за каким-нибудь Уведомлением, иначе они не будут использоваться. В простой команде содержится логика приложения. В макрокоманде содержатся другие команды
MacroCommand
import org.puremvc.as3.patterns.command.*;
import com.me.myapp.controller.*;
// A MacroCommand executed when the application starts.
public class StartupCommand extends MacroCommand
{
// Initialize the MacroCommand by adding its subcommands.
override protected function initializeMacroCommand() : void
{
addSubCommand( ModelPrepCommand );
addSubCommand( ViewPrepCommand );
}
}
Комманды в Макрокоманде выполняются в том порядке в каком вы их добавляете.
SimpleCommand
import org.puremvc.as3.patterns.observer.*;
import org.puremvc.as3.patterns.command.*;
import com.me.myapp.*;
import com.me.myapp.model.*;
// Create and register Proxies with the Model.
public class ModelPrepCommand extends SimpleCommand
{
// Called by the MacroCommand
override public function execute( note : INotification ) : void
{
facade.registerProxy( new SearchProxy() );
facade.registerProxy( new PrefsProxy() );
facade.registerProxy( new UsersProxy() );
}
}
Модель
Proxy
import org.puremvc.as3.patterns.proxy.Proxy;
public class UserProxy extends Proxy implements IProxy {
}
Модель pureMVC состоит из набора Прокси объектов, которые использются для управления различными моделями данных приложения. Прокси управляют доступом к локальным или удаленным источникам данных. Они могут за данными обращаться к другим объектам или хранить их в себе. При изменении данных Прокси могут рассылать Уведомления другим заинтересованним в этом объектам (как правило Посредникам).
Прокси работающие с удаленными источниками данных могут инкапсулировать взаимодействие с HTTPService. Они запрашивает данные асинхронно и после получения ответа отсылают Уведомления.
import com.undr.priceEditorClient.model.util.URLList;
import com.undr.priceEditorClient.model.vo.UserVO;
import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
import mx.rpc.http.HTTPService;
import org.puremvc.as3.interfaces.*;
import org.puremvc.as3.patterns.proxy.Proxy;
public class UserProxy extends Proxy implements IProxy, IResponder {
public static const NAME:String = "UserProxy";
private var _currentUrl:String
private var _userVO:UserVO;
public function UserProxy() {
super( NAME, null );
}
public function get user():UserVO {
return this._userVO;
}
public function login(item:UserVO):void {
var service:HTTPService = new HTTPService();
_currentUrl = URLList.AUTH_POST_LOGIN;
service.url = URLList.makeURL(_currentUrl);
service.method = "POST";
service.resultFormat = "object";
var token:AsyncToken = service.send(item as Object);
token.addResponder(this);
}
public function logout():void {
var service:HTTPService = new HTTPService();
_currentUrl = URLList.AUTH_POST_LOGOUT;
service.url = URLList.makeURL(_currentUrl, this.user.sessionId);
service.method = "POST";
service.resultFormat = "object";
var token:AsyncToken = service.send(_userVO as Object);
token.addResponder(this);
}
public function result(data:Object):void {
var remoteUser:Object;
switch(_currentUrl){
case URLList.AUTH_POST_LOGIN:
if(data.result.response.header.status == "ok") {
remoteUser = data.result.response.header.user;
this._userVO = new UserVO;
this._userVO.fullName = remoteUser.fullname;
this._userVO.role = remoteUser.role;
this._userVO.accountName = remoteUser.name;
sendNotification(ApplicationFacade.LOGIN_SUCCESS, this.user);
} else {
sendNotification(ApplicationFacade.LOGIN_FAILURE, data.result.response.header.message);
}
break;
case URLList.AUTH_POST_LOGOUT:
if(data.result.response.header.status == "ok") {
remoteUser = data.result.response.header.user;
this._userVO = new UserVO;
this._userVO.fullName = remoteUser.fullname;
this._userVO.role = remoteUser.role;
this._userVO.accountName = remoteUser.name;
sendNotification(ApplicationFacade.LOGOUT_SUCCESS, data.result.response.header.message);
} else {
sendNotification(ApplicationFacade.LOGOUT_FAILURE, data.result.response.header.message);
}
break;
default:
}
}
public function fault(info:Object):void {
switch(_currentUrl){
case URLList.AUTH_POST_LOGIN:
sendNotification(ApplicationFacade.LOGIN_FAILURE, "Ошибка соединения");
break;
case URLList.AUTH_POST_LOGOUT:
sendNotification(ApplicationFacade.LOGOUT_FAILURE, "Ошибка соединения");
break;
default:
}
}
}
Value Object
Используются для хранения данных (см. Value Objects)
Представления (View)
Посредники
Компоненты не могут напрамую обмениваться Уведомлениями с Прокси и могут выполнять команды потому что не имеют метода sendNotification(). Для того, чтобы они смогли это сделать, необходимы Посредники. Они отлавливают события компонентов и передают их дальше в виде Уведомлений. Так же они подписаны на Уведомления cвязнанные с компонентом и при трансляции такого Уведомления изменяют данные компонента.
При создании посредника в конструктор передается объект компонента. В конструкторе происходит подписка на события компонента.
import flash.events.Event;
import org.puremvc.as3.interfaces.*;
import org.puremvc.as3.patterns.mediator.Mediator;
public class ApplicationMediator extends Mediator implements IMediator {
public static const NAME:String = "ApplicationMediator"
public function ApplicationMediator(viewComponent:priceEditorClient) {
super(NAME, viewComponent);
app.addEventListener(priceEditorClient.LOGIN, appLoginHandler);
app.addEventListener(priceEditorClient.LOGOUT, appLogoutHandler);
app.addEventListener(priceEditorClient.EDIT_PRICELIST, appEditPricelistHandler);
app.addEventListener(priceEditorClient.LOAD_PRICELIST, appLoadPricelistHandler);
app.addEventListener(priceEditorClient.SAVE_PRICELIST, appSavePricelistHandler);
}
private function appLoginHandler(event:Event):void {
this.sendNotification(ApplicationFacade.LOGIN_ACTION, app.userVO);
}
}
Подписка на Уведомления происходит в перегруженном методе listNotificationInterests(). Посредник подписан на те сообщения, которые присутствуют в масиве возращенном методом.
return [
ApplicationFacade.LOGIN_SUCCESS,
ApplicationFacade.LOGOUT_SUCCESS,
ApplicationFacade.LOGIN_FAILURE,
ApplicationFacade.LOGOUT_FAILURE,
ApplicationFacade.TREE_LOADED_SUCCESS,
ApplicationFacade.TREE_LOADED_FAILURE
];
}
Обработчик Уведомлений в зависимости от имени Уведомления выполняет требуемый код
switch ( note.getName() ) {
case ApplicationFacade.LOGIN_SUCCESS:
var userVO:UserVO = note.getBody() as UserVO;
app.userLabel = userVO.fullName+" ("+userVO.role+")";
app.currentState = "";
break;
case ApplicationFacade.LOGOUT_SUCCESS:
app.userLabel = "";
app.currentState = "authState";
break;
case ApplicationFacade.LOGIN_FAILURE:
Alert.show(note.getBody() as String);
app.currentState = "authState";
break;
case ApplicationFacade.LOGOUT_FAILURE:
Alert.show(note.getBody() as String);
break;
case ApplicationFacade.TREE_LOADED_SUCCESS:
app.currentState = "";
app.mainViewStack.selectedIndex = 1;
break;
case ApplicationFacade.TREE_LOADED_FAILURE:
break;
}
}
Компоненты
В основном mxml файле приложения присутствует, как правило, ViewStack. Компоненты являются его дочерними объектами. У каждого свой Посредник.
pureMVC библиотека очень маленькая в ней нет ничего лишнего, но тем не менее она очень помогает содержать код в чистоте и не мешать мухи с котлетами.


На AS3 не писал, но с PureMVC знаком, правда слабо представляю себе веб-приложение на связке PureMVC+Ruby. А вот порт на Java (http://trac.puremvc.org/PureMVC_Java_MultiCore) в связке с GWT+ExtGWT (можно и без последнего) – оказывается весьма вкусным))
Musthave
6 Фев 10 at 5:59
Ну он и на AS3 вкусный. И с Ruby работает через AMF, сам то я, правда, не пробовал. Так только эксперементировал.
Я его с PHP связовал, через WebORB. Давненько это было.
undr
6 Фев 10 at 9:27
эээ… AMF и WebORB – это для клиент-серверного обмена, сам-то клиент остается на AS3.. Я имел в виду, что порт на Ruby мне не понятен в плане применимости в действительных веб-приложениях.
Musthave
6 Фев 10 at 11:18
А, понял. Ну тут я мало чего могу сказать. Я не знаком с PureMVC на Ruby. Но то, что он мало распространнен о чем-то говорит.
undr
6 Фев 10 at 12:31