原创  PureMVC软件架构分析与鉴赏 收藏

PureMVC软件架构分析与鉴赏

        李俊杰

概述

随着客户端程序的风起云涌,Adobe公司收购了Macromedia公司,针对Flesh的客户端进行研发,推出了Flex语言。Flex语言是类似与java的面向对象语言,编码格式又吸收了Delphi的编码风格,尽管Flex可以展现Flesh的炫目的客户端效果,全新的用户体验,但针对企业化的应用,仅有Flex是远远不够的,需要有合理的软件架构规范,于是Flex世界的MVC框架就应运而生了,PureMVCFlex世界中的MVC模式的具体实现,相对于adobe官方的CairngormFlex MVC框架,PureMVC更加简单实用。

PureMVC的整体架构

从使用者角度上讲,PureMVC设计的非常合理,对外的接口是Façade,由Façade来组织整个应用,其命名规范也显而易见,可以望文生义。其中ControllerModelView是管理ICommandIProxyIMediator的容器,而ICommandIProxyIMediato是应用系统要扩展的MVC各层的接口。

这样,使用者就可以方便地扩展FaçadeCommandProxyMediator,来完成MVC各层的功能,没有特殊的需要,一般无需关注ModelViewController核心代码。

PureMVC的分层设计理念

PureMVC的各个层次各负其责,Façade实现统一管理,定义了系统的统一接口,使用者无需了解底层代码的运作方式。 Model层是实现与后台交互的层次,由Model类来管理Proxy,当然在Flex中也支持直接访问数据库,但大多数业务系统中,都是Flex作为客户端程序,而后台由Java或者.net来完成真正的业务操作。View层是显示层,在PureMVC中由Mediator实现对Viewcomponent的操作,如界面的简单校验等功能。Controller层是控制层,主要来处理一些数据转换等工作。下面具体介绍各层的具体功能及设计

Façade,外部应用的接口界面

从下图可以看出,Façade是对外的接口,同时管理ModelView, Controller,同时Façadesingleton

1)在Java中实现singleton模式,构造方法都使用private方法,而这儿使用了public,但看第一句代码,呵呵!效果是一样的。但最好不要直接调用这个public方法

public function Facade( ) {

                     if (instance != null) throw Error(SINGLETON_MSG);

                     instance = this;

                     initializeFacade();   

              }

2)模板模式的应用

protected function initializeFacade(  ):void {

                     initializeModel();

                     initializeController();

                     initializeView();

              }

Façade的构造方法中使用了模板模式,虽然Façade本身也实现了这些初始化方法,但都是获得ModelControllerView,在具体应用中就可以initializeController()这三个方法,来注册相关的ProxyCommandMediator等。注意,实际上override initializeController()方法是比较常见的,而不常override initializeModel()initializeView() 最常见的是在Commandexecute()中创建并注册Proxy,或者创建并注册Mediator

3)如下的IFacade接口中,其中的ProxyCommandMediator管理方法都是借助于ModelControllerView的管理方法来实现的。

4Façade是实现INotifier接口的,所以Façade可以向系统发出消息或者通知,因为Flex是客户端的程序语言,其调用方式也是类似与Windows方式的观察者(observer)模式,Façade是统一的接口界面,所以几乎所有的外部调用都是通过sendNotification()方法。这个方法的调用的是notifyObservers()方法,而这个方法是如下的:

public function notifyObservers ( notification:INotification ):void {

                     if ( view != null ) view.notifyObservers( notification );

              }

通常你只需要调用sendNotification()。

下面具体介绍PureMVCMVC各层的架构设计:

Model Proxy

1)如上所述,Model也是Singleton设计模式,也无需使用new的方式创建,另外在企业应用中也很少直接扩展这个类,如果需要扩展,可以扩展initializeModel(),就相当于对Model的扩展了。

public function Model( )

              {

                     if (instance != null) throw Error(SINGLETON_MSG);

                     instance = this;

                     proxyMap = new Array();    

                     initializeModel();    

              }

2Model是管理Proxy的容器类,使用类似与Java中的HashMap的数据类Array,来管理Proxy,代码类似:

protected var proxyMap : Array;

proxyMap[ proxy.getProxyName() ] = proxy;

包括后面谈到的ControllerView,都是这样管理其对象的,如获取某个值对象,

return proxyMap[ proxyName ];//简单吧,呵呵!

3)在registerProxy()和removeProxy()方法中,分别调用了ProxyonRegister()onRemove()方法,看到这儿,你是不是感到很熟悉。onXxxx()的命名规则,表示由子类实现详细内容来override父类的内容。抽象的原则是:共性化的东西向上(父类)抽象,个性化的东西向下(子类)延伸。即在实际应用中ProxyonRegister()onRemove()这两个方法是用来扩展我们自己的功能代码的。

Model

public function registerProxy( proxy:IProxy ) : void

            {

                   proxyMap[ proxy.getProxyName() ] = proxy;

                   proxy.onRegister();

            }

Proxy

public function onRegister( ):void {}

4Proxy是我们要扩展的类,可以看出Proxy类实现了数据的交互,setData()和getData()方法实现数据的装载和获取,onRegister( )onRemove( )可以实现相关Proxy注册和注销时的功能。

5proxyName,是唯一表示系统中这个Proxy的名称,如上所述,Model管理Proxy是依赖类似与JavaHashMapArray,所以必须是key->value(名值对),类似这样的设计还包括MediatorCommandFaçade等。

6Proxy还实现了INotifier接口,所以也可以向外发送通知(消息),后面会具体讲解观察者模式在Flex中的广泛应用。

7Proxy继承了Notifier类,在Notifier类中有Protected属性façade,这个Façade是系统中单例,所以可以通过这个Façade来注册相关的Mediator

View Mediator

1)类似于Model View是管理Mediator的容器类,但View除了管理Mediator以外,还管理Observer,后面会专门讲Observer,其他如MediatorCommandProxyFaçadeObserver的注册都最终到View中来管理。

2View作为管理MediatorObserver的容器,利用的就是FlexArray对象,Array对象如上面所说的JavaHashMap,也类似与Java中的Stack,其有push(), pop()等方法,

public function registerObserver ( notificationName:String, observer:IObserver ) : void

              {

                     var observers:Array = observerMap[ notificationName ];

                     if( observers ) {

                            observers.push( observer );

                     } else {

                            observerMap[ notificationName ] = [ observer ];

                     }

              }

这儿使用了push()方法,可以看出和Java HashMap的差异,HashMapkey/value,而Flex Array中一个key,可以对应多个Value,事实上是一个通知(消息),对应多个观察者(1..n)。

3Mediator作为应用中要扩展的对象,被View容器管理,根据FlexArray特性,每个Mediator必须有唯一的名称mediatorName,作为key,保存在Array中,同样Mediator也是继承了Notifier类,这样拥有Façade protected属性,可以注册相关CommandProxy;同时又实现了INotifier接口,可以向外发送通知(消息)。

4Mediator作为容器来管理ViewComponent,这样就可以把方便地控制界面展现。

Controller Command

1)  类似于ModelViewController也是Command的容器,通过FlexArray来管理Command,同时Controller也是单例模式。

2)  Command管理中,涉及到Observer监听的问题。

public function registerCommand( notificationName : String, commandClassRef : Class ) : void

              {

                     if ( commandMap[ notificationName ] == null ) {

                            view.registerObserver( notificationName, new Observer( executeCommand, this ) );

                     }

                     commandMap[ notificationName ] = commandClassRef;

              }

从这儿可以看出,凡是需要提供Observer监听功能的,都需要通过View来注册。

3)  Command的执行方法,即当Command被通知时,是调用Commandexecute方法

public function executeCommand( note : INotification ) : void

              {

                     var commandClassRef : Class = commandMap[ note.getName() ];

                     if ( commandClassRef == null ) return;

 

                     var commandInstance : ICommand = new commandClassRef();

                     commandInstance.execute( note );

              }

4)  MacroCommandSimpleComman都实现了ICommand接口,其中MacroCommand实现了对ICommand的递归调用,这儿采用了类似于树的设计。

public final function execute( notification:INotification ) : void

              {

                     while ( subCommands.length > 0) {

                            var commandClassRef : Class = subCommands.shift();

                            var commandInstance : ICommand = new commandClassRef();

                            commandInstance.execute( notification );

                     }

              }

其中subCommands就是一个Command数组。

5)  可以在protected function initializeMacroCommand():void中做如下扩展

override protected function initializeMacroCommand( ) : void

                            {

                            addSubCommand( com.me.myapp.controller.FirstCommand );

                            addSubCommand( com.me.myapp.controller.SecondCommand );

                            addSubCommand( com.me.myapp.controller.ThirdCommand );

                            }

这样,当Command初始化时就会来调用这个方法,因为你加入到Command数组中可能是SimpleCommand,那在执行时就直接执行它的execute方法,如果你加入的是MacroCommand对象,则执行MacroCommandexecute的递归方法。

 

观察者模式的应用

1)在PureMVC中大量使用了Notifier来完成发通知(消息)的机制,类似与java的调用,但和java的直接调用不同,是依赖观察者模式来实现的。

2)在Observer类中实现了设置上下文(调用哪个类?)和设置处理通知(消息)方法,这样一旦有消息发出,则捕获该消息,调用相应的类的某个方法来响应这个消息。

public function Observer( notifyMethod:Function, notifyContext:Object )

              {

                     setNotifyMethod( notifyMethod );

                     setNotifyContext( notifyContext );

              }

这个构造方法来初始化哪个类的那个方法来响应消息,就相当于把某个类封装到Observer内部。

3)如何来应用呢?我们来看Controller类中关于注册Command的一段代码

view.registerObserver( notificationName, new Observer( executeCommand, this ) );

View作为系统内部的唯一实例,来注册Observer,第一个参数是要响应的通知名称(如“Login”),第二个参数是创建一个Observer对象,该对象封装了响应的类是thisController)的方法“executeCommand”,而executeCommand方法从参数INotification中获取通知的名称,再根据名称从Controller中找到相应的Command来执行它的execute方法。

调用例子简介

上面讲了Observer的基本原理,为了更清晰地表现观察者模式的调用,如下图所示,调用的流程如下:

1)  创建ActionFacade的实例_facadeActionFacadeFaçade的实现类,并注册相关的Command,如在观察者模式讲到的,把相关的Command封装到Observer中,并注册到View内,其响应的通知名称为“login

override protected function initializeController( ) : void

        {

            super.initializeController();

                    

            registerCommand( STARTUP, StratCommand );

            this.registerCommand( LOGIN, LoginCommand );

          

        }

2)  调用_facade.login(user)

public function login( user:UserVo ):void

        {

               sendNotification( LOGIN, user );

        }

3)  实际上是调用父类FaçadesendNotification方法

public function sendNotification( notificationName:String, body:Object=null, type:String=null ):void

              {

                     notifyObservers( new Notification( notificationName, body, type ) );

              }

Façade方法notifyObservers

public function notifyObservers ( notification:INotification ):void {

                     if ( view != null ) view.notifyObservers( notification );

              }

也就是说要调用ViewnotifyObservers方法

4)  ViewnotifyObservers方法如下,遍历所有关注这个通知(名称)的Observer,依次执行这些ObservernotifyObserver方法。

   public function notifyObservers( notification:INotification ) : void

              {

                     if( observerMap[ notification.getName() ] != null ) {

                            var observers:Array = observerMap[ notification.getName() ] as Array;

                            for (var i:Number = 0; i < observers.length; i++) {

                                   var observer:IObserver = observers[ i ] as IObserver;

                                   observer.notifyObserver( notification );

                            }

                     }

              }

5)  下面来看ObservernotifyObserver方法

public function notifyObserver( notification:INotification ):void

              {

                     this.getNotifyMethod().apply(this.getNotifyContext(),[notification]);

              }

这个方法很简单,获取这个Observer封装的响应通知的方法(如exectue()),并把对应的上下文(如Controller),和通知作为参数来执行,呵呵!很像java中的反射机制。

6)  如在观察者模式的应用中讲到的,执行了ControllerexecuteCommand方法,从而遍历Command数组,找到响应这个通知的Command(如LoginCommand),并执行这个Commandexectue方法。

总结

PureMVC相对来说,比较简单,对于初学者来说比较容易理解和应用,也可以作为学习设计模式的简易教材。同时作为Flex的框架性代码,从另外的角度可以学习Flex编程思想及设计思路。

 

努力,在于我热爱我的事业,与中国的软件一起走向成熟,走向世界。

   

联系作者:lijj_72@hotmail.com

发表于 @ 2008年07月10日 14:47:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:航海日志(2) ----from Oracle to DB2 | 新一篇:JPA 与 Websphere的兼容性问题解决方案

  • 发表评论
  • 评论内容:
  •  
Copyright © lijj_72
Powered by CSDN Blog