Gitlib Gitlib
首页
  • 分类
  • 标签
  • 归档
  • Golang开发实践万字总结
  • MySQL核心知识汇总
  • Redis实践总结
  • MQ实践万字总结
  • Docker数据持久化总结
  • Docker网络模式深度解读
  • 常用游戏反外挂技术总结
  • 读书笔记
  • 心情杂货
  • 行业杂谈
  • 友情链接
关于我
GitHub (opens new window)

Ravior

以梦为马,莫负韶华
首页
  • 分类
  • 标签
  • 归档
  • Golang开发实践万字总结
  • MySQL核心知识汇总
  • Redis实践总结
  • MQ实践万字总结
  • Docker数据持久化总结
  • Docker网络模式深度解读
  • 常用游戏反外挂技术总结
  • 读书笔记
  • 心情杂货
  • 行业杂谈
  • 友情链接
关于我
GitHub (opens new window)
  • PHP

  • Golang

  • Python

  • Javascript

    • PureMVC

      • PureMVC(JS版)源码解析:认识PureMVC
      • PureMVC(JS版)源码解析01:观察者模式解析
      • PureMVC(JS版)源码解析02:Notification类
      • PureMVC(JS版)源码解析03:Observer类
      • PureMVC(JS版)源码解析04:Notifier类
      • PureMVC(JS版)源码解析05:SimpleCommand类
      • PureMVC(JS版)源码解析06:MacroCommand类
      • PureMVC(JS版)源码解析07:Mediator类
      • PureMVC(JS版)源码解析08:Proxy类
      • PureMVC(JS版)源码解析09:View类
        • PureMVC(JS版)源码解析10:Controller类
        • PureMVC(JS版)源码解析11:Model类
        • PureMVC(JS版)源码解析12:Facade类
        • PureMVC(JS版)源码解析:总结
      • 从一个基础Javascript面试题谈起
      • Javascript垃圾回收机制
      • Javascript实现双向数据绑定
      • 浏览器渲染网页过程
      • 浏览器资源缓存机制总结
    • 其他语言

    • 编程语言
    • Javascript
    • PureMVC
    Ravior
    2013-10-11
    目录

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

    PureMVC

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

    在PureMVC中M、V、C三部分由三个单例类管理,分别是Model/View/Controller。PureMVC中另外一个单例类Facade。Facade提供了与MVC三个单例类(核心类)通信的唯一接口。这4个单例类构建了PureMVC的骨架。

    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引用。

    PureMVC

    View.prototype.mediatorMap = null;
    
    
    1
    2

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

    {
        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();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

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

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

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

    View.prototype.registerObserver = function(notificationName, observer)                 
    {                                                                                      
        if(this.observerMap[notificationName] != null)                                     
        {                                                                                  
            this.observerMap[notificationName].push(observer);                             
        }                                                                                  
        else                                                                               
        {                                                                                  
            this.observerMap[notificationName] = [observer];                               
        }                                                                                  
    };        
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    View类的observerMap属性主要用于存放消息名(notificationName)和observer(观察者)之间的映射,一个消息名可以对应几个observer,所以如果检索到observerMap中存在notificationName,则把observer推入相应的数组中。observerMap大致的数据格式如下:

    {“notificationName1":[observer1,observer2],"notificationName2":[observer3,observer4]} 
    
    1

    同时,除了注册观察者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];                         
        }                                                                      
    };          
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    另外,我们知道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;                                                        
    };                                                                          
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

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

    View.prototype.retrieveMediator = function(mediatorName)             
    {                                                                    
        return this.mediatorMap[mediatorName];                           
    };  
    
    
    1
    2
    3
    4
    5

    通过上面的例子,我们可以知道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);
            }
        }
    };
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    到目前为止,我们应该可以大致弄清楚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;
    };
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    根据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];
    };
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 总结

    View类的结构相对于Mediator、Command、Proxy类要复杂许多,但它是PureMVC消息机制的核心,里面的很多方法需要记住,反复揣摩,特别是notifyObservers()。

    #PureMVC
    上次更新: 2022/12/01, 11:09:34
    PureMVC(JS版)源码解析08:Proxy类
    PureMVC(JS版)源码解析10:Controller类

    ← PureMVC(JS版)源码解析08:Proxy类 PureMVC(JS版)源码解析10:Controller类→

    最近更新
    01
    常用游戏反外挂技术总结
    11-27
    02
    Golang开发实践万字总结
    11-11
    03
    Redis万字总结
    10-30
    更多文章>
    Theme by Vdoing | Copyright © 2011-2022 Ravior | 粤ICP备17060229号-3 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式