[理解实践PureMVC框架]3-MVC三个核心层的具体实现

上一节将这三个核心层需要的类都实现了,这节就讲下Model、View、Controller这三层的具体实现。这三个层都用到了单例模式。

Model层:
我们并不需要在这个层里保存任何数据,所有数据都是通过代理来获取操作的,这个层只需要保存所有代理的引用就可以了。具体的数据类型可以用另外的类来声明,然后将这个数据保存到代理中。该层提供方法注册、获取、移除代理。

/*
 *	Model.ooc
 *	数据层
 */

import structs/HashMap

import interfaces/IModel
import patterns/proxy/Proxy

Model: class implements IModel {
	// 一些脚本语言中的数据使用非常方便,但是ooc-lang中的数据没这么方便
	// 所以用一个哈希表来保存所有代理
	proxyMap: HashMap

	// 类的唯一实例
	instance: static Model

	SINGLETON_MSG := "Model Singleton already constructed!"

	init: func {
		// 为构造函数添加一些限制,使其只能实例化一个类的实例
		if (This instance != null) {
			Exception new(SINGLETON_MSG) throw()
		}
		"[ Model ] new()" println()

		instance = this
		proxyMap = HashMap new()
		initializeModel()
	}

	// 初始化Model层。一般情况下我们只需要用这一个Model层就行了
	// 如果我们需要自己实现一个具体的Model层,就要重写这个方法,在这里边进行一些初始化操作
	initializeModel: func {
		"[ Model ] initializeModel()" println()
	}

	// 获取类的唯一实例,声明为静态函数
	getInstance: static func -> Model {
		if (This instance == null) {
			This instance = Model new()
		}
		return This instance
	}

	// 注册一个代理,参数就是一个代理的实例了
	registerProxy: func(proxy: Proxy) {
		"[ Model ] registerProxy() #{proxy getProxyName()}" println()

		proxyMap add(proxy getProxyName(), proxy)
		proxy onRegister()
	}

	// 通过代理名称获取代理的实例
	retrieveProxy: func(proxyName: String) -> Proxy {
		"[ Model ] retrieveProxy() #{proxyName}" println()

		proxyMap get(proxyName)
	}

	// 查询是否存在这个代理
	hasProxy?: func(proxyName: String) -> Bool {
		proxyMap get(proxyName) != null
	}

	// 从代理列表中移除这个代理
	removeProxy: func(proxyName: String) -> Proxy {
		"[ Model ] removeProxy() #{proxyName}" println()

		proxy := proxyMap get(proxyName)
		if (proxy != null) {
			proxyMap remove(proxyName)
			proxy onRemove()
		}
		return proxy
	}
}

View层:
该层只负责保存中介和观察者的引用,并提供方法注册、获取、移除中介或观察者。而具体的界面元素,一般是放在另一个单独的类里,然后在代理中保存该类的引用。

/*
 *	View.ooc
 *	界面层
 */
 
import structs/[HashMap, ArrayList]

import interfaces/IView
import patterns/mediator/Mediator
import patterns/observer/[Observer, Notification]

View: class implements IView {
	// 中介列表,用于保存中介的引用
	mediatorMap: HashMap
	// 观察者列表,用于保存观察者的引用
	observerMap: HashMap>

	// 类的唯一实例
	instance: static View

	SINGLETON_MSG := "View Singleton already constructed!"

	init: func {
		// 限制构造函数,使其只能实例化一个类实例
		if (This instance != null) {
			Exception new(SINGLETON_MSG) throw()
		}
		"[ View ] new()" println()

		This instance = this
		mediatorMap = HashMap new()
		observerMap = HashMap> new()
		initializeView()
	}

	// 初始化View层。一般情况下我们只需要用这一个View层就行了
	// 如果我们需要自己实现一个具体的View层,就要重写这个方法,在这里边进行一些初始化操作
	initializeView: func {
		"[ View ] initializeView()" println()
	}

	// 获取类的唯一实例,声明为静态函数,这样就可以不用实例化类也能访问该函数
	getInstance: static func -> View {
		if (This instance == null) {
			This instance = View new()
		}
		return This instance
	}

	// 注册一个观察者,参数为对应的通知名称和一个观察者实例
	registerObserver: func(notificationName: String, observer: Observer) {
		"[ View ] registerObserver() #{notificationName}" println()

		// 从观察者列表中获取对应通知的观察者链表,一个通知可以注册多个观察者
		observerList := observerMap get(notificationName)
		// 如果这个链表不为空
		if (observerList != null) {
			// 直接将观察者添加到这个链表中
			observerList add(observer)
		} else {
			// 否则先建一个链表,再添加
			observerList = ArrayList new()
			observerList add(observer)
			observerMap add(notificationName, observerList)
		}
	}

	// 通知观察者,参数为对应的通知
	notifyObservers: func(notification: Notification) {
		"[ View ] notifyObservers() #{notification getName()}" println()

		// 先判断对应的观察者列表是否为空
		if (observerMap get(notification getName()) != null) {
			// 官方的AS3版中,将观察者列表复制了一份,而不是在原来的引用上调用观察者的方法
			// 原因是说避免在调用的过程中修改观察者的引用?这个我也不确定会不会改变
			observerList := observerMap get(notification getName()) clone()

			// 遍历每个观察者,并调用它的方法执行对应的通知函数
			observerList each(|observer| {
				observer notifyObserver(notification)
			})
		}
	}

	// 移除观察者
	removeObserver: func(notificationName: String, notifyContext: Pointer) {
		"[ View ] removeObserver() #{notificationName}" println()

		observerList := observerMap get(notificationName)
		if (observerList != null) {
			for (i in 0..observerList getSize()) {
				observerList removeAt(i)
			}
			observerMap remove(notificationName)
		}
	}

	// 注册一个中介
	registerMediator: func(mediator: Mediator) {
		// 避免重复注册
		if (mediatorMap get(mediator getMediatorName()) != null) {
			return
		}
		"[ View ] registerMediator() #{mediator getMediatorName()}" println()

		mediatorMap add(mediator getMediatorName(), mediator)

		// 获取该中介感兴趣的通知
		interests := mediator listNotificationInterests()

		// 如果有感兴趣的通知,需要注册对应的观察者来执行通知函数。
		if (interests length > 0) {
			// 使用闭包来解决类成员方法作为参数时会默认多一个this指针作为参数,但是声明为static方法却无法重写时的问题
			executeCommand := func(notification: Notification) {
				mediator handleNotification(notification)
			}
			// 新建一个观察者
			observer := Observer new(executeCommand, mediator)

			// 为每个感兴趣的通知注册一个观察者
			for (i in 0..interests length) {
				registerObserver(interests[i], observer)
			}
		}

		mediator onRegister()
	}

	// 根据中介名获取对应的中介
	retrieveMediator: func(mediatorName: String) -> Mediator {
		"[ View ] retrieveMediator() #{mediatorName}" println()

		mediatorMap get(mediatorName)
	}

	// 从中介列表中移除对应的中介
	removeMediator: func(mediatorName: String) -> Mediator {
		"[ View ] removeMediator() #{mediatorName}" println()

		mediator := mediatorMap get(mediatorName)

		// 移除中介的过程中,还需把中介注册了的观察者给移除,因为不需要用到了
		if (mediator != null) {
			interests := mediator listNotificationInterests()

			if (interests length > 0) {
				for (i in 0..interests length) {
					removeObserver(interests[i], mediator)
				}
			}

			mediatorMap remove(mediatorName)

			mediator onRemove()
		}

		return mediator
	}

	// 查询中介列表中是否有对应的中介
	hasMediator?: func(mediatorName: String) -> Bool {
		mediatorMap get(mediatorName) != null
	}
}

Controller层:
该层是负责Model层和View层的沟通的,具体的逻辑都分布在了一个个的Command类中,它只保存这些命令的引用,并提供注册、移除命令的方法。它本身并没有写其它逻辑的。

/*
 *	Controller.ooc
 *	控制层
 */
 
import structs/HashMap

import interfaces/[IController, IView, ICommand, INotification]
import core/View
import patterns/observer/[Observer, Notification]

Controller: class implements IController {
	// view层的引用,因为需要用到里边注册观察者的方法
	view: View
	// 命令列表,用于保存所有命令的实例
	commandMap: HashMap

	// 类的唯一实例
	instance: static Controller
		
	SINGLETON_MSG := "Controller Singleton already constructed!"

	init: func {
		// 限制构造函数,使其只能实例话一个类实例
		if (This instance != null) {
			Exception new(SINGLETON_MSG) throw()
		}
		"[ Controller ] new()" println()

		This instance = this
		commandMap = HashMap new()
		initializeController()
	}

	// 初始化Controller层。一般情况下我们只需要用这一个Controller层就行了
	// 如果我们需要自己实现一个具体的Controller层,就要重写这个方法,在这里边进行一些初始化操作
	initializeController: func {
		"[ Controller ] initializeController()" println()

		view = View getInstance()
	}

	getInstance: static func -> Controller {
		if (This instance == null) {
			This instance = Controller new()
		}
		return This instance
	}

	// 执行对应通知的命令,会作为一个观察者的回调函数
	executeCommand: func(notification: Notification) {
		"[ Controller ] executeCommand() #{notification getName()}" println()

		// 先从命令列表里获取对应的命令
		command := commandMap get(notification getName())
		// 不存在的话直接返回
		if (command == null) {
			return
		}
		// 存在就执行,不管是SimpleCommand还是MacroCommand,只需执行execute()这个方法就行了
		command execute(notification)
	}

	// 注册一个命令,参数为通知名称和对应的命令实例
	registerCommand: func(notificationName: String, command: ICommand) {
		"[ Controller ] registerCommand() #{notificationName}" println()

		// 这里的逻辑和官方AS3版的差不多,但是我感觉官方的逻辑有点点问题
		// 看下面的代码可以知道,一个通知名称只能注册一个观察者,但是View层注册观察者的
		// 方法有说-一个通知可以注册多个观察者的,但是这里的逻辑只能注册一个
		// 我不清楚是不是故意这样做的
		if (commandMap get(notificationName) == null) {
			// 这里将executeCommand这个方法重新封装了一下,是为了避免直接将它作为回调传给
			// observer的话,会默认传入一个this指针的问题,导致回调函数类型不匹配
			exeCmd := func(notification: Notification) {
				executeCommand(notification)
			}
			// 注册一个观察者监听这个通知
			view registerObserver(notificationName, Observer new(exeCmd, this))
		}
		// 添加到命令列表
		commandMap add(notificationName, command)
	}

	// 查询是否有对应的命令
	hasCommand?: func(notificationName: String) -> Bool {
		commandMap get(notificationName) != null
	}

	// 移除对应的命令
	removeCommand: func(notificationName: String) {
		"[ Controller ] removeCommand() #{notificationName}" println()

		if (hasCommand?(notificationName)) {
			// 同时也要移除对应的观察者
			view removeObserver(notificationName, this)

			commandMap remove(notificationName)
		}
	}
}

核心层代码就这样,ooc-lang实现的和官方AS3版的有点点区别,但逻辑都是一样的。下一节把Facade类讲完就没了。

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