这节讲下这几个类的实现,这里先不讲外观类,我决定把MVC这三层讲完再讲外观类,因为MVC这三层的实现需要这几个类,而外观类的实现又涉及到MVC这三层。
虽然代码的逻辑是跟官方的一样的,比如类名和方法名是完全一样的,但是如果你用另一门语言去实现的话,还是会遇到一些问题需要解决的,这是因为不同语言间的特性还是有点区别的。有些东西,并不是你觉得会了,实现起来就如你想的那般,只有亲自实践了,才能体会到其中的不同(和你想象中的不同)。当然,还有一点重要的,通过用别的语言自己实现一遍,可以加深你的理解。这里我用的语言是ooc-lang。
这些类都是基类,基类的作用就是将共有的、不变的功能独立出来,如果你需要一个新的类,这个类既需要基类的功能又需要新的功能,就直接继承基类,然后实现新功能就可以了。因此这几个类的作用就是这样的。
命令类:
命令类有两种,一种是只执行一个命令的,另一种是可以顺序执行多个命令的。命令模式是为了分解Controller层的,使得View层和Model层的交互通过具体的命令类,而不是Controller,这样就不用把所有逻辑都集中到一个Controller了,而是分成一个个的命令。所有命令都注册保存在Controller里边。
/*
* SimpleCommand.ooc
* 单个命令类。和官方AS3的差不多,只是参数类型用的不是接口,然后用的是继承而不是实现
* 前面的文章有说过,这是因为ooc-lang的特性问题所决定的
*/
import patterns/observer/Notification
import interfaces/[ICommand, INotifier]
// 这个类就只有一个execute()方法,在这个方法里边可以注册或获取中介类或代理类的实例
// 也可以发通知,它没有状态,只是执行一些逻辑,按我的理解就是:它执行完毕后,下一次
// 再执行,不关上一次什么事了,并不需要用到上一次的状态
SimpleCommand: class extends ICommand{
// 其实这个方法并不需要具体的实现,因为每个命令的逻辑都是不一样的,所以都是在派生类里实现具体的逻辑的
// 感觉这里是强行用了一种固定的思想:就是不管需不需要实现,我都定义一个基类先
execute: func(notification: Notification) {}
}
/*
* MacroCommand.ooc
* 顺序执行多个命令的类
*/
import structs/LinkedList
import interfaces/ICommand
import patterns/command/SimpleCommand
import patterns/observer/Notification
// 有时需要同时顺序执行多个命令,这时就继承这个类来实现具体的逻辑就行了
MacroCommand: class extends ICommand {
// 用来保存多个子命令
subCommands: LinkedList
// 构造函数,子类继承后需要调用这个父类的构造函数来初始化
init: func {
subCommands = LinkedList new()
initializeMacroCommand()
}
// 这个方法是继承后需要我们重写的,作用就是添加子命令
initializeMacroCommand: func {}
// 添加子命令的方法,继承后可以直接调用
addSubCommand: func(command: SimpleCommand) {
subCommands add(command)
}
// MacroCommand类的执行方法,这个不需要重写。它其实就是调用所有子命令的execute()方法
// 命令的执行顺序是:先添加的先执行
execute: final func(notification: Notification) {
"[ MacroCommand] execute() #{notification getName()}" println()
while (subCommands getSize() > 0) {
cmd := subCommands removeAt(0)
cmd execute(notification)
}
}
}
通知类和观察者类:
通知类就是描述一个通知是怎样的,观察者类就是负责监听某个通知并执行相应的方法的。我这里没有通知者Notifier这个类了,因为这个类的逻辑我在上一节的INotifier接口中实现了(ooc-lang的接口允许有实现)。观察者模式是为了避免各模块间的直接调用,而是通过发布订阅的模式来间接调用的,以此来解耦各个模块。
/*
* Notification.ooc
* 通知类
*/
import interfaces/INotification
// 这个类其实也没什么,一看就知
Notification: class implements INotification {
// 一个通知包含名称、类型和消息体,消息体就是传参用的
name: String
type: String
body: Pointer // ooc-lang中无法保存object,所以用Pointer就可以保存任意类型的参数了
init: func(name: String, body: Pointer = null, type: String = null) {
this name = name
this body = body
this type = type
}
getName: func -> String {
name
}
setBody: func(body: Pointer) {
this body = body
}
getBody: func -> Pointer {
body
}
setType: func(type: String) {
this type = type
}
getType: func -> String {
type
}
// 这个方法是用来打印整个通知的内容的,但是由于ooc-lang并不能将一个类的实例字符化
// 所以body就无法知道它里边有什么了
toString: func -> String {
msg: String = "Notification Name: " + getName()
msg += "\nBody:" + ((body == null) ? "null" : "body")
msg += "\nType:" + ((type == null) ? "null" : type)
return msg
}
}
/*
* Observer.ooc
* 观察者类
*/
import interfaces/IObserver
import patterns/observer/Notification
// 一个观察者就观察一个通知,它保存有通知执行的方法,收到通知时就立马执行该方法
Observer: class implements IObserver {
// 通知执行的方法,它是一个接收一个类型为Notification的参数的函数
notify: Func(Notification)
// 上下文环境,一些脚本语言中,会有this问题,当一个类的方法作为回调给另一个类的方法调用时
// 会有this指向不明确的问题,所以需要在注册一个观察者时就保存好这个上下文环境
// 在调用回调的时候再指明这个上下文环境,但是ooc-lang中好像并不需要
context: Pointer
init: func(notify: Func(Notification), context: Pointer) {
setNotifyMethod(notify)
setNotifyContext(context)
}
setNotifyMethod: func(notify: Func(Notification)) {
this notify = notify
}
setNotifyContext: func(context: Pointer) {
this context = context
}
getNotifyMethod: func -> Func(Notification) {
notify
}
getNotifyContext: func -> Pointer {
context
}
// 通知观察者
notifyObserver: func(notification: Notification) {
// 收到通知后立马执行回调函数
getNotifyMethod()(notification)
}
compareNotifyContext: func(object: Pointer) -> Bool {
object == context
}
}
中介类:
通过一个或多个中介来管理一个界面,使得不用把所有界面的更新显示逻辑集中到一个View里。所有的中介都注册保存到View里边。
/*
* Mediator.ooc
* 中介类
*/
import patterns/observer/Notification
import interfaces/[INotifier, IMediator]
// 中介自身也可以发通知,所以继承了通知者这个类
Mediator: class extends INotifier implements IMediator {
NAME: static String = "Mediator"
// 每个中介都有名称,这样可以通过名称来获取具体的中介
mediatorName: String
// 每个中介都可以保存一个界面的引用,这样就可以访问界面的元素了
viewComponent: Pointer
init: func(mediatorName: String = null, viewComponent: Pointer = null) {
this mediatorName = mediatorName == null ? NAME : mediatorName
this viewComponent = viewComponent
}
getMediatorName: func -> String {
mediatorName
}
setViewComponent: func(viewComponent: Pointer) {
this viewComponent = viewComponent
}
getViewComponent: func -> Pointer {
viewComponent
}
// 列出这个中介感兴趣的通知,在注册中介的时候需要用到。该方法在具体的实现类重写
listNotificationInterests: func -> String[] {
[] as String[]
}
// 这个方法里边会写出所有列出的感兴趣的通知需要执行的逻辑,比如界面更新逻辑
// 在注册中介时,会将该方法作为一个观察者的回调函数。子类具体实现时重写
handleNotification: func(notification: Notification) {
"Mediator handleNotification()" println()
}
// 中介注册是回调,可重写
onRegister: func {
"[ Mediator ] onRegister() #{getMediatorName()}" println()
}
// 中介移除时回调,可重写
onRemove: func {
"[ Mediator ] onRemove() #{getMediatorName()}" println()
}
}
代理类:
通过代理模式,将Model层的数据细分,不必集中到一个Model里,使得数据更容易复用,改写的时候更方便。所有的代理都注册保存到Model里边。
/*
* Proxy.ooc
* 代理类
*/
import interfaces/[IProxy, INotifier]
// 代理自身也需要发通知,所以继承了通知者类。具体的子类实现时,可以有其它的逻辑在里边。
Proxy: class extends INotifier implements IProxy {
NAME: static String = "Proxy"
// 每个代理都有自己的名称,这样可以通过名称获取具体的代理
proxyName: String
// 该变量用来保存数据的引用
data: Pointer
init: func(proxyName: String = null, data: Pointer = null) {
this proxyName = proxyName == null ? NAME : proxyName
if (data != null) {
setData(data)
}
}
getProxyName: func -> String {
proxyName
}
setData: func(data: Pointer) {
this data = data
}
getData: func -> Pointer {
data
}
onRegister: func {
"[ Proxy ] onRegister() #{getProxyName()}" println()
}
onRemove: func {
"[ Proxy ] onRemove() #{getProxyName()}" println()
}
}
这几个基类的代码就这样了,不多,如果理解整个框架的执行步骤的话,就可以把这些代码串起来的。下一节就会讲到MVC这三个核心层了。