[理解实践PureMVC框架]2-命令、通知、观察者、代理、中介类的具体实现

这节讲下这几个类的实现,这里先不讲外观类,我决定把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这三个核心层了。

你可能感兴趣的:(PureMVC框架)