PureMVC(JS版)源码解析09: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;
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();
}
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);
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];
}
};
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]}
同时,除了注册观察者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];
}
};
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;
};
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];
};
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);
}
}
};
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;
};
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];
};
2
3
4
5
6
7
8
9
10
11
12
13
# 总结
View类的结构相对于Mediator、Command、Proxy类要复杂许多,但它是PureMVC消息机制的核心,里面的很多方法需要记住,反复揣摩,特别是notifyObservers()
。