PureMVC AS3 源码分析

PureMVC不仅仅是AS的经典MVC模式的框架,同时也有其他语言的实现,比如JS,php,C#。我接触PureMVC也只是这半年里的事,伴随着一个项目应用了这个边学边用的AS3框架。PureMVC很容易使用,那时我参考的是Cliff Hall的Best Practices,例子讲述了一个使用PureMVC的登陆过程。非常详细,而且很多注意点提示地都非常深刻。

PureMVC不是基于flash本身的事件机制,而是采用了一种发送通知的观察者机制。这里主要分析这种通知的机制。

首先是INotifier.as

public interface INotifier  
{
    function sendNotification( notificationName:String, body:Object=null, type:String=null ):void; 
}

这个类只有一个sendNotification()方法。我们知道,PureMVC里全部的模块(command, mediator, proxy, facade)都可以发送通知,所以它们都实现了INotifier接口。所不同的是proxy不接受任何的通知,它只是送出通知。所以对于proxy来说,它不注册监视通知并作出动作的observer。

Observer类直接实现了IObserver接口,后者只有简单的4个方法定义。Observer则在这4个方法的基础上增加了对应类成员的get方法。对于Observer类来说,最重要的是notifyObserver()方法。

public function notifyObserver( notification:INotification ):void  
{
    this.getNotifyMethod().apply(this.getNotifyContext(),[notification]);
}

这个方法将调用对应于某个对象的方法来接受并处理通知。 对于mediator来说,就是调用其自身的handleNotification()方法。这点可以从View.as中看到。

// Create Observer referencing this mediator's handlNotification method
var observer:Observer = new Observer( mediator.handleNotification, mediator );  

而对于command来说,就是调用Controller的executeCommand()方法,这个方法实例化对应的command然后调用其execute()去具体执行一个通知行为。 在controller.as中,registerCommand()里调用的是view的registerObserver()

public function registerCommand( notificationName : String, commandClassRef : Class ) : void  
{
    if ( commandMap[ notificationName ] == null ) {
        view.registerObserver( notificationName, new Observer( executeCommand, this ) );
    }
    commandMap[ notificationName ] = commandClassRef;
}

当observer通知执行某个command时,首先调用的就是controller的executeCommand(),随后这个方法里,依据commandMap的映射关系,找出对应于该通知的command,然后实例化,调用execute()执行。

public function executeCommand( note : INotification ) : void  
{
    var commandClassRef : Class = commandMap[ note.getName() ];
    if ( commandClassRef == null ) return;

    var commandInstance : ICommand = new commandClassRef();
    commandInstance.execute( note );
}

那么notifyObserver()是由谁调用的?作为整个observer的核心调度方法,View类的notifyObservers()将通知派发到全部相关的observer那里。

public function notifyObservers( notification:INotification ) : void  
{
    if( observerMap[ notification.getName() ] != null ) {

        // Get a reference to the observers list for this notification name
        var observers_ref:Array = observerMap[ notification.getName() ] as Array;

        // Copy observers from reference array to working array, 
        // since the reference array may change during the notification loop
        var observers:Array = new Array(); 
        var observer:IObserver;
        for (var i:Number = 0; i < observers_ref.length; i++) { 
            observer = observers_ref[ i ] as IObserver;
            observers.push( observer );
        }

        // Notify Observers from the working array              
        for (i = 0; i < observers.length; i++) {
            observer = observers[ i ] as IObserver;
            observer.notifyObserver( notification );
        }
    }
}

首先,获取对应某个通知的observer的列表引用。这个observerMap是在注册observer时被赋值的一个通知对应observer数组的数组。当用通知名索引这个observerMap时得到的就是一个相关observer的数组。 然后拷贝这个相关observer数组,因为持有这个数组的引用是不够的,这个数组可能会在整个通知过程中被更改。 最后,对每个observer调用自身的notifyObserver()方法。这样原先注册过的这个通知的方法就会开始执行。到这里,整个观察者机制就已经差不多了。

回过头来看Facade,真是应有尽有!不仅持有全部核心类的引用,所有添加、移除、获取方法一应俱全~所以Facade无所不能,在整个PureMVC里起到一个核心作用。它调用相应的model、controller、view的方法,而这几个核心类,各自管理着自己的那部分模块。

PureMVC AS3很简练,很轻量,10个接口,11个类,代码简洁而优雅,虽然在应用上可能是有局限性的(有人说它高不成低不就),但它绝对是一个优秀的框架


评论加载中...

Disqus提供评论支持,如果评论长时间未加载,请飞跃长城。