框架的使用,无非就是为了代码的重用和解耦,使得工程易于理解、维护或重构等。
那PureMVC这个框架是如何达到这些目的的呢?我就以自己的理解简单讲下。我的理解肯定是学习了解别人的介绍后自己做出的总结,别人的介绍可能又是学习别人的理解来的。因此,我的理解可能和别人的差不多,然后会有点点自己的总结在里边。当然,我会在理解后,自己设计一些例子或写个代码进行演示,这样更助于理解。
其实学习了解别人的理解并不一定就会快,或比较省事,别人的理解毕竟是按他自己的情况理解的,每个人的情况不一样。但是我们可以通过学习多个人的理解,来总结出自己的理解。如果你没接触过一类事物,就算你看别人的理解,对于你来说也是一个新概念,别人的理解只是给你一种思路去如何学习。
传统的MVC框架中:Model层就是负责存放数据,它没有操作。View层只负责界面的显示逻辑。而Controller层呢,就是当Model层的数据发生改变后,获取到相关数据并计算好,然后更新View层相关的数据显示,其实它就相当于View层和Model层的一个沟通桥梁。
比如有个游戏的主界面,它需要显示一些角色名、金币、经验等数据。然后按MVC的框架设计,我们把这些数据通通放到一个Model层,界面的元素和显示逻辑通通放到一个View层,操作数据和更新界面数据的逻辑通通放到Controller层。但是我们想一下,有些数据是所有界面共用的,像角色名,金币这些,可能不止这一个界面需要用到,如果我们为每个界面都单独写3个MVC层,那代码就重复了(没达到代码重用的理念)。当然我们可以在其它界面直接调用一个界面的MVC层,这样就不用重复写代码了,但是,这样还会有什么问题呢?不同的界面,它们之间可能有共用的数据,还会有特有的数据,如果我们在一个界面的MVC层直接调用另一个界面的MVC层的话,会导致耦合度变高(没实现解耦)。
因此,用传统MVC框架去写的话,如果一个界面越复杂,MVC层的代码就会越多,然后会变的越复杂,越难控制,界面与界面间的代码就会越耦合。而PureMVC框架,就是为了解决这一问题的。下面我再简单介绍下PureMVC中各个层的作用,当然,如果你喜欢更官方、最原始的说法,可以直接到官网看说明,官网也有介绍。如果你觉得官方的“太官方”了,可以看下我的,或其他人的。
PureMVC本质上还是一个MVC框架,它只是围绕MVC这3个层做了扩展,加入了一些设计模式,使得代码更方便重用和解耦。在PureMVC中,涉及到的设计模式有:命令模式、代理模式、中介模式、观察者模式、外观模式和单例模式(设计模式的使用我就不讲了)。
- 通过命令模式,将Controller层的逻辑分解成一个个独立的小的命令,然后Controller层只负责保存这些命令的引用,而这些命令的执行是通过通知,也就是观察者模式来触发的,一个命令对应着一个通知,当收到通知时,对应的命令就会自动执行(不用我们手动调用)。通知和命令是通过映射关联起来的。可以在命令类里头注册中介类和代理类。命令自身也可以发送通知。
- 通过代理模式,将Model层的数据分解成更独立的模块,然后Model层只负责保存这些代理的引用,外界通过数据的代理类来获取相应的数据。比如金币代理、经验代理等,其它地方就统一访问这个代理来获取数据了。当然也别分的太细,不然显得啰嗦。代理自身也可以发送通知。
- 通过中介模式,将View层的界面元素跟相关逻辑分离出来,然后View层只负责保存这些中介的引用。View层还可以保存观察者类,一个观察者对应了一个通知需要执行的命令。如果一个界面太复杂的话,可以为其设置多个中介类来管理更新这个界面。中介类负责监听相关通知来更新界面的数据显示和其它界面逻辑。中介类可以获取并访问代理类。中介自身也可以发送通知。
- 观察者模式是供MVC这3层之间交流的,这样它们之间就不用直接调用其它模块的方法了,而是通过通知,间接执行相关方法。从而达到模块间的解耦。当然,观察者模式用太多的话,调试起来会比直接调用的麻烦。
- 外观模式其实就是封装了MVC这3层的方法,它并没有实现额外的东西,这样做的目的是为了提供一个统一的方式给外界使用,而不用关心MVC这3层的具体实现。
- 单例模式用的比较多,而且用起来比较方便。它是为类提供一个实例,这样我们在使用这个类的时候,不用担心是否实例化或会不会实例化多个。
各个层之间的关系差不多就是这样了。在使用PureMVC这个框架的过程中,我发现一个明显的缺点就是:它封装的太多了,以致于执行一个简单的步骤,都需要一层层的执行。
接下来看下各个模块的接口。其实ooc-lang中的接口并没有其它传统的OOP语言的好用,在实现框架的过程中并无多大作用,差不多就是用来声明给人看一下的,不过我还是用了接口,因为有个地方需要用到,也可以使代码结构跟官方的统一。至于ooc-lang的接口的缺点,可以找我另一篇介绍ooc-lang的文章了解。
接口相当与C语言中的头文件吧,它只提供声明,不提供实现,然后可以方便的作为参数类型,Java中还可以通过接口实现多继承等。
先讲INotifier.ooc
、INotification.ooc
和IObserver.ooc
这三个模块吧,其它几个模块都需要继承或需要用到的。
/*
* INotifier.ooc
* INotifier类就是用来发送通知的,它不做其它事。其它类只需继承该类就可以发通知了
*/
import patterns/facade/Facade
import patterns/observer/Notification
// 虽然说了是接口啊,但是这里我把它定义为了一个抽象类,因为ooc-lang中的接口不太一样,也无法多继承
// 所以我用抽象类来解决这问题了
INotifier: abstract class {
// 其实我感觉PureMVC这个框架的Notifier类不太合理,虽然我这里代码和原来的不完全一样,但整个过程是一样的
// 发通知的过程是这样的:调用Facade这个单例中的发通知方法,而Facade中的方法又是调用
// View中的方法来发通知的,最后才会通知到Observer那里
// 这就导致,要这里能正确发通知的话,得确保View这个模块实现正确,然后Facade这个模块实现也正确,最后这里才正确
// 所以我感觉这里耦合度还是挺高的
facade: Facade {
get {
return Facade getInstance()
}
}
// 这个方法的实现,是和官方的一样的
// 参数分别是通知名,通知的消息体(需要传递的参数放这里),通知的类型
sendNotification: func(notificationName: String, body: Pointer = null, type: String = null) {
facade notifyObservers(Notification new(notificationName, body, type))
}
}
/*
* INotification.ooc
* 通知的接口
*/
// 一个通知包括通知名,通知的消息体和通知类型了
INotification: interface {
getName: func -> String
setBody: func(body: Pointer)
getBody: func -> Pointer
setType: func(type: String)
getType: func -> String
toString: func -> String
}
/*
* IObserver.ooc
* 观察者接口
*/
import patterns/observer/Notification
// 一个观察者包含一个对应的执行方法,当收到通知时,就执行观察者里的方法
IObserver: interface {
setNotifyMethod: func(notifyMethod: Func(/*Pointer, */Notification))
setNotifyContext: func(notifyContext: Pointer)
notifyObserver: func(notification: Notification)
compareNotifyContext: func(object: Pointer) -> Bool
}
接下来就是命令接口了:
/*
* ICommand.ooc
* 命令接口
*/
import patterns/observer/Notification
import interfaces/INotifier
// 这里我同样将它声明为了一个抽象类,我需要将它作为参数传给函数。原因还是因为ooc-lang中的接口问题导致的
// 命令当然需要一个执行方法
ICommand: abstract class extends INotifier {
// 参数是一个通知类,命令执行的时候可以获取通知里的相关内容
execute: func(notification: Notification)
}
接下来是代理接口:
/*
* IProxy.ooc
* 代理接口,和官方AS3的一致,只是将对应的数据类型改成了ooc-lang中的
*/
// 一个代理当然包括代理的数据了,然后还有代理类的名称
IProxy: interface {
getProxyName: func -> String
setData: func(data: Pointer)
getData: func -> Pointer
onRegister: func
onRemove: func
}
再接下来的中介类接口:
/*
* IMediator.ooc
* 中介类,和官方AS3的差不多
*/
import patterns/observer/Notification
// 一个中介包含了对界面的元素的引用,就是viewComponent了,然后还有中介类的名
IMediator: interface {
getMediatorName: func -> String
getViewComponent: func -> Pointer
setViewComponent: func(viewComponent: Pointer)
// 列出中介感兴趣的通知,在注册中介的时候需要用到
listNotificationInterests: func -> String[]
// 在注册中介的时候,会根据中介感兴趣的通知来注册一个观察者,下面这个方法就是观察者收到通知后调用的了
handleNotification: func(notification: Notification)
onRegister: func
onRemove: func
}
Controller接口:
/*
* IController.ooc
* 和官方AS3的差不多
*/
import ICommand
import patterns/observer/Notification
// Controller类提供方法来注册、执行、移除、查询命令
IController: interface {
// 注册命令时需要和通知名称对应起来
registerCommand: func(notificationName: String, command: ICommand)
executeCommand: func(notification: Notification)
removeCommand: func(notificationName: String)
hasCommand?: func(notificationName: String) -> Bool
}
View接口:
/*
* IVeiw.ooc
* 和官方AS3的差不多
*/
import patterns/mediator/Mediator
import patterns/observer/Notification
import patterns/observer/Observer
// View中保存观察者和中介的引用,所以提供方法用来注册、移除、通知观察者,注册、移除、查询、获取中介
IView: interface {
registerObserver: func(notificationName: String, observer: Observer)
removeObserver: func(notificationName: String, notifyContext: Pointer)
notifyObservers: func(notification: Notification)
registerMediator: func(mediator: Mediator)
retrieveMediator: func(mediatorName: String) -> Mediator
removeMediator: func(mediatorName: String) -> Mediator
hasMediator?: func(madiatorname: String) -> Bool
}
Model接口:
/*
* IModel.ooc
* 和官方AS3的一致
*/
import patterns/proxy/Proxy
// Model类保存数据的代理,不直接保存数据,所以它提供方法注册、查询、移除代理
IModel: interface {
registerProxy: func(proxy: Proxy)
retrieveProxy: func(proxyName: String) -> Proxy
removeProxy: func(proxyName: String) -> Proxy
hasProxy?: func(proxyName: String) -> Bool
}
最后就是Facade接口了:
/*
* IFacade.ooc
* 和官方AS3的差不多
*/
import interfaces/INotifier
import patterns/proxy/Proxy
import patterns/mediator/Mediator
import patterns/observer/Notification
// Facade类只是封装MVC这3层的方法,它并没有做其它事
// 我没有继承INotifier啊,因为ooc-lang的语言特性原因,但是Facade类还是可以发通知的,因为
// 在实现类里有继承INotifier
IFacade: interface {
registerProxy: func(proxy: Proxy)
retrieveProxy: func(proxyName: String) -> Proxy
removeProxy: func(proxyName: String) -> Proxy
hasProxy?: func(proxyName: String) -> Bool
registerCommand: func(notificationName: String, command: Pointer)
removeCommand: func(notificationName: String)
hasCommand?: func(notificationName: String) -> Bool
registerMediator: func(mediator: Mediator)
retrieveMediator: func(mediatorName: String) -> Mediator
removeMediator: func(mediatorName: String) -> Mediator
hasMediator?: func(mediatorName: String) -> Bool
notifyObservers: func(notification: Notification)
}
整个框架的接口就是这样了,和官方的有点点区别。然后应该可以注意到,某些方法我并没有返回一个接口,而是直接返回一个实现类,因为ooc-lang中接口不好用(似乎我说了很多次这话了…)。还有方法的参数类型我也是用实现类的。
后面的文章就是讲具体的实现了。全篇完。?