PureMVC(JS版)源码解析(九):View类

     在讲解View类之前,我们先回顾一下PureMVC的模块划分:

     在PureMVC中M、V、C三部分由三个单例类管理,分别是Model/View/Controller。PureMVC中另外一个单例类——Facade。Facade提供了与MVC三个单例类(核心类)通信的唯一接口。这4个单例类构建了PureMVC的骨架。
     在游戏开发中,一个游戏是由多个模块组成,如主场景模块,战斗模块等等,每个模块通常都是单独的Model/View/Controller,显然PureMVC的4个单例类是无法满足需求的,它就提供了Proxy/Mediator/Command来解决问题。
     Proxy/Mediator/Command分别对应MVC中的Model/View/Controller,也分别有对应的单例管理,Model保存所有的Proxy引用、View保存所有的Mediator引用,Controller保存所有的Command映射。
     这篇博客开始,我们就要开始讲解PureMVC的三大核心类,先看看View类。
     View保存了所有的Mediator引用,它有一个mediatorMap数组,用来存放所有的Mediator引用。
View.prototype.mediatorMap = null;

     我们知道Mediator类有一个onRegister()方法,当Mediator对象在facade中注册时调用的,实际上Mediator对象的注册是通过调用View单例的registerMediator()方法来实现。

View.prototype.registerMediator = function(mediator)
{
    if(this.mediatorMap[mediator.getMediatorName()] != null)
    {
        return;
    }
    //mediator类继承Notifier类,这是初始化Notifier,给mediator的facade属性赋值
    mediator.initializeNotifier(this.multitonKey);
    this.mediatorMap[mediator.getMediatorName()] = mediator;

    //返回mediator感兴趣的消息
    var interests = mediator.listNotificationInterests();
    // register mediator as an observer for each notification
    if(interests.length > 0)
    {
        //创建一个Observer,把notification和mediator关联起来
        var observer = new Observer(mediator.handleNotification, mediator);
        for(var i = 0; i < interests.length; i++)
        {
            this.registerObserver(interests[i], observer);
        }
    }
    //在这里调用了mediator的onRegister()方法
    mediator.onRegister();
}

从这段代码中可以明白Mediator类onRegister()方法的调用机制了,这段代码中还有一个地方需要我们去深入研究:

//这段代码什么意思呢?
this
.registerObserver(interests[i], observer);

registerObserver(),通过方法名我们可以知道它是用来注册Observer对象的,我们看一下的实现代码:

View.prototype.registerObserver = function(notificationName, observer)                 
{                                                                                      
    if(this.observerMap[notificationName] != null)                                     
    {                                                                                  
        this.observerMap[notificationName].push(observer);                             
    }                                                                                  
    else                                                                               
    {                                                                                  
        this.observerMap[notificationName] = [observer];                               
    }                                                                                  
};                                                                                     

View类的observerMap属性主要用于存放消息名(notificationName)和observer(观察者)之间的映射,一个消息名可以对应几个observer,所以如果检索到observerMap中存在notificationName,则把observer推入相应的数组中。observerMap大致的数据格式如下:
{“notificationName1":[observer1,observer2],"notificationName2":[observer3,observer4]} 
 
同时,除了注册观察者【registerObserver()】,还需要从observerMap中移除观察者(removeObserver):
View.prototype.removeObserver = function(notificationName, notifyContext)  
{                                                                          
    //通过notificationName,可以检索到接受该消息的Observer,返回一个数组                                                                
    var observers = this.observerMap[notificationName];                    
    for(var i = 0; i < observers.length; i++)                              
    {                                                                      
        if(observers[i].compareNotifyContext(notifyContext) == true)       
        {      
            //移除observer                                                            
            observers.splice(i, 1);                                        
            break;                                                         
        }                                                                  
    }                                                                                                                                   
    if(observers.length == 0)                                              
    {                                                                      
        delete this.observerMap[notificationName];                         
    }                                                                      
};                                                                         

     另外,我们知道Mediator类还有一个与onRegister()(注册)方法对应的onRemove()(注销)方法,是Mediator对象在facade中注销时调用的,Mediator对象的注销是通过调用View单例的removeMediator()方法来实现:

View.prototype.removeMediator = function(mediatorName)                      
{                                                                           
    var mediator = this.mediatorMap[mediatorName];                          
    if(mediator)                                                            
    {                                                                       
        // for every notification the mediator is interested in...          
        var interests = mediator.listNotificationInterests();               
        for(var i = 0; i < interests.length; i++)                           
        {                                                                   
            // remove the observer linking the mediator to the notification                                               
            this.removeObserver(interests[i], mediator);                    
        }                                                                   
                                                                            
        // remove the mediator from the map                                 
        delete this.mediatorMap[mediatorName];                              
                                                                            
        //触发mediator对象的onRemove方法                  
        mediator.onRemove();                                                
    }                                                                       
                                                                            
    return mediator;                                                        
};                                                                          

     mediator对象除了在facade中注册(registerMediator),从facade中注销(removeMediator),还有一个很重要的方法,就是从facade里面检索mediator( retrieveMediator):

View.prototype.retrieveMediator = function(mediatorName)             
{                                                                    
    return this.mediatorMap[mediatorName];                           
};                                                                                                                                     

     通过上面的例子,我们可以知道Mediator类onRegister(),onRemove()方法的使用原理(与View类的registerMediator(),removeMediator,retrieveMediator()对应)和怎么注册观察者(registerObserver())、怎么移除观察者(removeMediator())。View类还有一个重要的方法就是给所有的观察者发送消息(notifyObservers()),触发观察者的消息处理函数。

View.prototype.notifyObservers = function(notification)
{
    // SIC
    if(this.observerMap[notification.getName()] != null)
    {
        var observers_ref = this.observerMap[notification.getName()], observers = [], observer

        for(var i = 0; i < observers_ref.length; i++)
        {
            observer = observers_ref[i];
            observers.push(observer);
        }

        for(var i = 0; i < observers.length; i++)
        {
            observer = observers[i];
            //出发了观察者的消息处理函数
            observer.notifyObserver(notification);
        }
    }
};

      到目前为止,我们应该可以大致弄清楚mediator对象的消息处理机制了。

      我们在Mediator/Command/Proxy通过调用继承自Notifier类的sendNotification()发送消息,实际上是调用View单例的notifyObservers()方法。

     View类是个多例类,它用instanceMap来存放View类的实例,我们来看一下它的构造函数:

function View(key)
{
    if(View.instanceMap[key] != null)
    {
        throw new Error(View.MULTITON_MSG);
    };

    this.multitonKey = key;
    View.instanceMap[this.multitonKey] = this;
    this.mediatorMap = [];
    this.observerMap = [];
    this.initializeView();
};

/**
 * @protected
 * Initialize the Singleton View instance
 * 
 * Called automatically by the constructor, this is your opportunity to
 * initialize the Singleton instance in your subclass without overriding the
 * constructor
 * 
 * @return {void}
 */
View.prototype.initializeView = function()
{
    return;
};

 

     根据Multiton模式(Multiton模式不理解的可以自行搜索)的设计原理,我们可以通过一个key值调用getInstance()方法来获取某个View实例:
View.getInstance = function(key)
{
    if (null == key)
        return null;
        
    if(View.instanceMap[key] == null)
    {
        View.instanceMap[key] = new View(key);
    };
//实际上是从instanceMap数组中检索
return View.instanceMap[key]; };

       总结一下,View类的结构相对于Mediator、Command、Proxy类要复杂许多,但他是PureMVC消息机制的核心,里面的很多方法我们需要记住,反复揣摩,特别是notifyObservers()。最后,附上View类的思维导图。

原文地址:https://www.cnblogs.com/iRavior/p/3362700.html