pureMVC使用笔记

框架Framework

开头重点必须说一下-----------观察者 Observer
这个类的主要功能:就是注册一个类的实例和类的方法,啥时候需要调用这个方法的时候,由观察者组织传参并且调用,在当前这个框架模式中,观察者是用的比较重要的一种设计模式,理解观察者模式,对于理解这个框架还是比较重要的
通常情况,我们调用一个类的某一个非静态方法,都是将这个类实例化,然后用这个实例来显式的调用这个方法,而观察者模式是预先将这个类的实例和一些特定非静态方法绑定,等外界需要调用的时候,会通知这个观察者来调用

local Observer = class('Observer')
function Observer:ctor(notifyMethod, notifyContext)
	self:setNotifyMethod(notifyMethod)
	self:setNotifyContext(notifyContext)
end

function Observer:setNotifyMethod(notifyMethod)
	self.notify = notifyMethod
end

function Observer:setNotifyContext(notifyContext)
	self.context = notifyContext
end

function Observer:getNotifyMethod()
	return self.notify
end

function Observer:getNotifyContext()
	return self.context
end

function Observer:notifyObserver(notification)
	self.notify(self.context, notification)
end

function Observer:compareNotifyContext(object)
	return object == self.context
end

return Observer

统一管理之Facade
内部持有Command,View,Model,三个管理员,这个类是个单例,主要是为了统一管理而存在
Facade:registerCommand:注册命令类的实例
Facade:registerProxy:注册数据类的实例
Facade:registerMediator:注册面板类的实例

Facade:sendNotification(notificationName, body, type):调用命令来启动命令实例或者面板实例的某个方法
Facade:retrieveProxy(proxyName):获取某个数据类,ps想要获取这个数据类的一些数据

local Controller = require( framework.PACKAGE_NAME .. '/core/Controller')
local Model = require( framework.PACKAGE_NAME .. '/core/Model')
local View = require( framework.PACKAGE_NAME .. '/core/View')
local Notification = require( framework.PACKAGE_NAME .. '/patterns/observer/Notification')

local Facade = class("Facade")

function Facade:ctor(key)
	if Facade.instanceMap[key] ~= nil then
		error(Facade.MULTITON_MSG)
	end
	self:initializeNotifier(key)
	Facade.instanceMap[key] = self
	self:initializeFacade()
end
function Facade:initializeFacade()
    self:initializeModel();
    self:initializeController();
    self:initializeView();	
end
function Facade.getInstance(key)
	if nil == key then
		return nil
	end
	if Facade.instanceMap[key] == nil then
		Facade.instanceMap[key] = Facade.new(key)
	end
	return Facade.instanceMap[key]	
end
function Facade:initializeController()
	if self.controller ~= nil then
		return
	end
	self.controller = Controller.getInstance(self.multitonKey);
end
function Facade:initializeModel()
	if self.model ~= nil then
		return
	end
	self.model = Model.getInstance(self.multitonKey)
end
function Facade:initializeView()
	if self.view ~= nil then
		return
	end
	self.view = View.getInstance(self.multitonKey)
end
function Facade:registerCommand(notificationName, commandClassRef)
	self.controller:registerCommand(notificationName, commandClassRef)	
end
function Facade:removeCommand(notificationName)
	self.controller:removeCommand(notificationName)
end
function Facade:hasCommand(notificationName)
	return self.controller:hasCommand(notificationName)
end
function Facade:registerProxy(proxy)
	self.model:registerProxy(proxy)
end
function Facade:retrieveProxy(proxyName)
	return self.model:retrieveProxy(proxyName)
end
function Facade:removeProxy(proxyName)
	local proxy = nil
	if self.model ~= nil then
		proxy = self.model:removeProxy(proxyName)
	end
	return proxy
end
function Facade:hasProxy(proxyName)
	return self.model:hasProxy(proxyName)
end
function Facade:registerMediator(mediator)
	if self.view ~= nil then
		self.view:registerMediator(mediator)
	end
end
function Facade:retrieveMediator(mediatorName)
	return self.view:retrieveMediator(mediatorName)
end
function Facade:removeMediator(mediatorName)
	local mediator = nil
	if self.view ~= nil then
		mediator = self.view:removeMediator(mediatorName)
	end
	return mediator
end
function Facade:hasMediator(mediatorName)
	return self.view:hasMediator(mediatorName)
end
function Facade:sendNotification(notificationName, body, type)
	g_lastMVCNotice = notificationName
	self:notifyObservers(Notification.new(notificationName, body, type))
end
function Facade:notifyObservers(notification)
	if self.view ~= nil then
		self.view:notifyObservers(notification)
	end
end
function Facade:initializeNotifier(key)
	self.multitonKey = key
end
function Facade.hasCore(key)
	return Facade.instanceMap[key] ~= nil
end
function Facade.removeCore(key)
	if Facade.instanceMap[key] == nil then
		return
	end
	Model.removeModel(key)
	View.removeView(key)
	Controller.removeController(key)
	Facade.instanceMap[key] = nil
end
Facade.instanceMap = {}

Facade.MULTITON_MSG = "Facade instance for this Multiton key already constructed!";
return Facade

Facade管理下的-------面板 View
该类主要维持两个数据,一个是面板实例数组,一个面板实例观察者数组
对于一个面板而言:在实例数组里村一份,然后它可能有若干个感兴趣的事件,但只会生产一个观察者,然后将每一个感兴趣的事件与这个观察者绑定
消息机制,外界去操作面板的唯一入口就是面板的handleNotification的这个方法,所以我们用面板实例和这个方法去创建一个观察者就可以了
该类注册完观察者以后,当外界有消息来时,会去通知观察者数组,来判断当前是否有这个消息的观察者,如果有就调用

function View:ctor(key)
	if View.instanceMap[key] ~= nil then
		error(View.MULTITON_MSG)
	end
	self.multitonKey = key
	View.instanceMap[self.multitonKey] = self
	self.mediatorMap = {}
	self.observerMap = {}
	self:initializeView()
end

function View:initializeView() end

function View.getInstance(key)
	if nil == key then
		return nil
	end
	if View.instanceMap[key] == nil then
		return View.new(key)
	else
		return View.instanceMap[key]		
	end
end

function View:registerObserver(notificationName, observer)
	if self.observerMap[notificationName] ~= nil then
		table.insert(self.observerMap[notificationName], observer)
	else
		self.observerMap[notificationName] = {observer}
	end
end

function View:notifyObservers(notification)
	if self.observerMap[notification:getName()] ~= nil then
		local observers_ref = self.observerMap[notification:getName()]
		for _, o in pairs(observers_ref) do
			o:notifyObserver(notification)
		end
	end
end

 function View:removeObserver(notificationName, notifyContext)
 	local observers = self.observerMap[notificationName]
 	for _, o in pairs(observers) do
 		if o:compareNotifyContext(notifyContext) then
 			table.remove(observers, _)
 			break
 		end
 	end

 	if #observers == 0 then
 		self.observerMap[notificationName] = nil
 	end
 end

function View:registerMediator(mediator)
	if self.mediatorMap[mediator:getMediatorName()] ~= nil then
		return
	end
	mediator:initializeNotifier(self.multitonKey)
	self.mediatorMap[mediator:getMediatorName()] = mediator
	local interests = mediator:listNotificationInterests()
	if #interests > 0 then
		local observer = Observer.new(mediator.handleNotification, mediator)
		for _, i in pairs(interests) do
			self:registerObserver(i, observer)
		end
	end

	mediator:onRegister()
end

function View:retrieveMediator(mediatorName)
	return self.mediatorMap[mediatorName]
end

function View:removeMediator(mediatorName)
	local mediator = self.mediatorMap[mediatorName]
	if mediator ~= nil then
		local interests = mediator:listNotificationInterests()
		for _, i in pairs(interests) do
			self:removeObserver(i, mediator)
		end
		self.mediatorMap[mediatorName] = nil
		mediator:onRemove()
	end
	return mediator
end

function View:hasMediator(mediatorName)
	return self.mediatorMap[mediatorName] ~= nil
end

function View.removeView(key)
	View.instanceMap[key] = nil
end

View.instanceMap = {}

View.MULTITON_MSG = "View instance for this Multiton key already constructed!"

Facade管理下的-------命令command
这个命令command和上面的view面板是大致相同的,也是维护一个命令实例数组,和生产一个观察者数组,只是这个观察者绑定的函数时当前命令的executeCommand这个函数,当外界发送消息通知观察者的时候,会调用当前命令的executeCommand这个函数,然后从当前命令实例数组中去找命令实例,如果存在则调用,这里需要注意,commond中用的view实例是单例,整个游戏正常情况也只有一个view单例,所以我们在facade中即使是注册命令,其最后生产的观察者也是存在同一个view实例中

local Controller = class('Controller')

function Controller:ctor(key)
	if Controller.instanceMap[key] ~= nil then
		error(Controller.MULTITON_MSG)
	end
	self.multitonKey = key	
	Controller.instanceMap[self.multitonKey] = self
	self.commandMap = {}	
	self:initializeController()
end

function Controller:initializeController()
	self.view = View.getInstance(self.multitonKey);
end

function Controller.getInstance(key)
	if nil == key then
		return nil
	end
	if(nil == Controller.instanceMap[key]) then
		return Controller.new(key)
	else
		return Controller.instanceMap[key]
	end
end

function Controller:executeCommand(note)
	local commandClassRef = self.commandMap[note:getName()]
	if(commandClassRef == nil) then
		return
	end
	local commandInstance = commandClassRef.new()
	commandInstance:initializeNotifier(self.multitonKey)
	commandInstance:execute(note)
end

function Controller:registerCommand(notificationName, commandClassRef)
	if(self.commandMap[notificationName] == nil) then
		self.view:registerObserver(notificationName, Observer.new(self.executeCommand, self));
	end
	self.commandMap[notificationName] = commandClassRef
end

function Controller:hasCommand(notificationName)
	return self.commandMap[notificationName] ~= nil
end

function Controller:removeCommand(notificationName)
	if self:hasCommand(notificationName) then
		self.view:removeObserver(notificationName, self)
		self.commandMap[notificationName] = nil
	end
end

function Controller.removeController(key)
	Controller.instanceMap[key] = nil
end

Controller.instanceMap= {}

Controller.MULTITON_MSG= "controller key for this Multiton key already constructed"

return Controller

Facade管理下的-------数据 Model
Model主要管理model实例,我们在游戏加载的时候,就实例化各个模块的model,最后调用的时候只需要retrieveProxy就可以使用了啊

local Model = class('Model')

function Model:ctor(key)
	if Model.instanceMap[key] then
 		error(Model.MULTITON_MSG)
 	end
 	self.multitonKey = key
 	Model.instanceMap[key] = self
 	self.proxyMap = {}
 	self:initializeModel()
end

function Model:initializeModel() end

function Model.getInstance(key)
	if nil == key then
		return nil
	end
	if Model.instanceMap[key] == nil then
		return Model.new(key)
	else
		return Model.instanceMap[key]		
	end
end

function Model:registerProxy(proxy)
	proxy:initializeNotifier(self.multitonKey)
	self.proxyMap[proxy:getProxyName()] = proxy
	proxy:onRegister()
end

function Model:retrieveProxy(proxyName)
	return self.proxyMap[proxyName]
end

function Model:hasProxy(proxyName)
	return self.proxyMap[proxyName] ~= nil
end

function Model:removeProxy(proxyName)
	local proxy = self.proxyMap[proxyName]
	if proxy ~= nil then
		self.proxyMap[proxyName] = nil
		proxy:onRemove()
	end
	return proxy
end

function Model.removeModel(key)
	Model.instanceMap[key] = nil
end

Model.instanceMap = {}

Model.MULTITON_MSG= "Model instance for this Multiton key already constructed!";

return Model

供外界继承的三剑客(model,view,control)

最顶层父类

local Facade = require( framework.PACKAGE_NAME .. '/patterns/facade/Facade')
local Notifier = class("Notifier")
function Notifier:ctor()
end
function Notifier:sendNotification(notificationName, body, type)
	local facade = self:getFacade()
	if facade ~= nil then
		facade:sendNotification(notificationName, body, type)
	end
end
function Notifier:initializeNotifier(key)
	self.multitonKey = key
	self.facade = self:getFacade()
end
function Notifier:getFacade()
	if self.multitonKey == nil then
		error(Notifier.MULTITON_MSG)
	end
	return Facade.getInstance(self.multitonKey)
end

Notifier.MULTITON_MSG = "multitonKey for this Notifier not yet initialized!";
return Notifier

视图
外界的视图类都应该继承此类
重写listNotificationInterests这个方法,将这个面板感兴趣的事件,列出来,比如面板的打开,面板的关闭,面板的刷新等
重写handleNotification这个方法,当有消息来的时候,会首先访问view的观察者数组,找到观察者,如果存在,那这个方法就会被框架调用

local Notifier = require( framework.PACKAGE_NAME .. '/patterns/observer/Notifier')
local Mediator = class('Mediator', Notifier)

function Mediator:ctor(mediatorName, viewComponent)
	self._mediatorName = mediatorName or Mediator.NAME
	self._viewComponent = viewComponent
end
Mediator.NAME = 'Mediator'
function Mediator:getMediatorName()
	return self._mediatorName
end
function Mediator:setViewComponent(viewComponent)
	self._viewComponent = viewComponent
end
function Mediator:getViewComponent()
	return self._viewComponent
end
function Mediator:listNotificationInterests()
	return {}
end
function Mediator:handleNotification(notification)
end
function Mediator:onRegister()
end
function Mediator:onRemove()
end

return Mediator

命令
对于既不是面板也不是数据的一些类,可以考虑继承此类,
execute:这个函数会被框架自动调用

ocal Notifier = require( framework.PACKAGE_NAME .. '/patterns/observer/Notifier')

local SimpleCommand = class('SimpleCommand', Notifier)

function SimpleCommand:ctor()
    SimpleCommand.super.ctor(self)  
end
function SimpleCommand:execute(notification)
end

return SimpleCommand

数据
重新onRegister()这个函数,将服务器消息注册一下,这个函数会被框架自动调用

local Notifier = require( framework.PACKAGE_NAME .. '/patterns/observer/Notifier')
local Proxy = class('Proxy', Notifier)
function Proxy:ctor(proxyName, data)
	self.proxyName = proxyName or Proxy.NAME
	if data ~= nil then
		self:setData(data)
	end
end
Proxy.NAME = "Proxy"
function Proxy:getProxyName()
	return self.proxyName
end
function Proxy:setData(data)
	self.data = data
end
function Proxy:getData()
    return self.data
end
function Proxy:onRegister()
end
function Proxy:onRemove()
end
return Proxy

基础使用

注册面板和发送面板消息
注册一个面板实例,同时将生成一个观察者,这个观察者绑定面板实例的一个方法handleNotification,这个方法非常重要,他是观察者操作这个面版实例的一个重要方法,每一个面板会存在若干个感兴趣的事件,而每一个事件名都会和这个观察者绑定,当外部触发某一个事件的时候,就会从队列中寻找是否存在这个事件,如果存在就会调用观察者来响应这次触发,即调用handleNotification这个函数,并且传参

--外界创建一个media的面板
local mediatorInst = Mediator.new()
facade:registerMediator(mediatorInst)

function Facade:registerMediator(mediator)
	if self.view ~= nil then
		self.view:registerMediator(mediator)
	end
end
function Facade:sendNotification(notificationName, body, type)
	g_lastMVCNotice = notificationName
	self:notifyObservers(Notification.new(notificationName, body, type))
end
function Facade:notifyObservers(notification)
	if self.view ~= nil then
		self.view:notifyObservers(notification)
	end
end

注册命令和发送命令消息

local StartUpCommand = requireCommand( "gamestates/StartUpCommand" )
global.Facade        = facade
facade:registerCommand( noticeTable.StartUp, StartUpCommand )
facade:sendNotification( noticeTable.StartUp )

注册数据和使用数据

 local Proxy     = requireProxy("PlayerProperty.lua")
 local proxyInst = Proxy.new()
 facade:registerProxy(proxyInst)
 local playerPropertyProxy = global.Facade:retrieveProxy(global.ProxyTable.PlayerProperty)
 local roleLevel = playerPropertyProxy:GetRoleLevel()

巧妙使用

在做游戏的时候,存在三个行为:
第一个行为:我们往往有数百个数据类,每一个数据类都有要和服务器交互的数据,当然在游戏中只需要require一个即可,可以考虑将这些数据类的引用路径单独放在一个表中,当我们启动游戏的时候,直接获取这个表,就可以完成所有数据类的注册;
第二个行为:数据类如此,面板类也是这样,游戏中也可能存在大量的面板,每一个面板都有一个media来管理,那么也可以生成一个media table来管理名字和引用路径,当启动游戏的时候,直接访问这个表,就可以注册所有的media;
第三个行为:控制类也是如此,也可以考虑将控制的名字和引用路径放到一张表中,在游戏启动的时候,将注册所有的controller

上面的三个行为,都和数据和视图无关,可以考虑将三个行为做成三个命令,每个命令就主要负责注册任务
形如:

--注册controller
function RegisterWorldControllerCommand:execute(note)
    local requireCommand    = requireCommand
    local registerTable     = requireCommand("register/WorldControllerTable")
    local facade            = global.Facade
    for k, v in pairs(registerTable) do
        local cmd = requireCommand(v)
        facade:registerCommand(k, cmd)
    end
end
--注册model
function RegisterWorldProxyCommand:registerProxy()
    local requireProxy    = requireProxy
    local registerTable   = requireCommand("register/WorldProxyTable")
    local facade          = global.Facade
    local size            = #registerTable
    local loadingUnit     = 89/size
    local function callback(param)
        local Proxy     = requireProxy(param)
        local proxyInst = Proxy.new()
        facade:registerProxy(proxyInst)
    end

    -- add loading task
    global.LoadingHelper:AddLoadingTask( registerTable, callback, loadingUnit, size )
end
--注册mediator
function RegisterWorldMediatorCommand:RegisterNormalLayerMediator()
    local requireMediator = requireMediator
    local registerTable = requireCommand("register/WorldMediatorTable")
    local facade = global.Facade

    local function callback(param)
        local Mediator = requireMediator(param)
        local mediatorInst = Mediator.new()
        facade:registerMediator(mediatorInst)
    end

    local nSize = #registerTable
    local loadingUnit = 10 / (nSize)

    -- add loading task
    global.LoadingHelper:AddLoadingTask(registerTable, callback, loadingUnit, nSize)
end
--触发加载
function LoadingBeginCommand:registerLogic()
    -- register 
    local RegisterWorldController = requireCommand( "register/RegisterWorldControllerCommand" )
    local RegisterWorldMediator   = requireCommand( "register/RegisterWorldMediatorCommand" )
    local RegisterWorldProxy      = requireCommand( "register/RegisterWorldProxyCommand" )
    local cmd = nil
    cmd = RegisterWorldController.new()
    global.Facade:registerCommand( global.NoticeTable.RegisterWorldController, cmd )
    cmd = RegisterWorldMediator.new()
    global.Facade:registerCommand( global.NoticeTable.RegisterWorldMediator, cmd )
    cmd = RegisterWorldProxy.new()
    global.Facade:registerCommand( global.NoticeTable.RegisterWorldProxy, cmd )
    global.Facade:sendNotification( global.NoticeTable.RegisterWorldController )
    global.Facade:sendNotification( global.NoticeTable.RegisterWorldProxy )
    global.Facade:sendNotification( global.NoticeTable.RegisterWorldMediator )
end

command和mediator争议

框架中对于这两个的处理其实差不多,他们的观察者都注册在同一个实例view中,他们也都是基于消息的,只是command只能处理一条消息,你可以理解为,当你发送command消息的时候,就会去通知当时注册的时候生成的观察者,然后观察者去执行取出绑定的函数和实例,这个函数其实就是controller的executeCommand函数,然后再从controller的命令实例数组中去找实例,最后执行execute,可以明显的感觉到,command只是执行一个消息,那mediator就不一样了,他对同样一个mediator实例可以注册若干个消息,所以mediator的处理能力要更大一些,但不管mediator有多少个感兴趣的事件,其最终还是一个mediator实例也只可以生成一个观察者,观察者都最终放在一个view实例数组中,通过key,value存储,key存储的是消息的名字,value存储的是观察者
分析到此,对于command和mediator的使用就可以自己选择了,一对一关系的可以选择使用command,比如注册等,一对多关系的可以使用mediator,比如UI面板,ui面板的打开,关闭,刷新就是有多条消息

mediator 应用场景之状态机

游戏的状态如下:

  GAME_STATE_ACTIVE = "active",             -- 启动
  GAME_STATE_PATCH  = "patch",              -- 补丁更新
  GAME_STATE_INIT   = "init",               -- 初始化
  GAME_STATE_LOGIN  = "login",              -- 登陆
  GAME_STATE_ROLE   = "role",               -- 选角
  GAME_STATE_WORLD  = "world",              -- 游戏世界

可以考虑生成一个继承mediator的GameStateController.lua
如下:负责管理的状态切换,状态在切换的时候就干了两件事,进入和关闭,而我们在外界只要发消息,该控制就可以收到消息,然后处理状态的进入和关闭

local GameStateController = class('GameStateController', framework.Mediator)
GameStateController.NAME  = "GameStateController"

local gameStateActive = require( "gamestates/gameStateActive" )
local gameStateInit   = require( "gamestates/gameStateInit" )
local gameStateLogin  = require( "gamestates/gameStateLogin" )
local gameStateRole   = require( "gamestates/gameStateRole" )
local gameStateWorld  = require( "gamestates/gameStateWorld" )
function GameStateController:ctor()
    GameStateController.super.ctor( self, self.NAME )
    self:Init()
end
function GameStateController:listNotificationInterests()
    local noticeTable = global.NoticeTable
    return {
        noticeTable.ChangeGameState,
        noticeTable.ReleaseMemory,
    }
end
function GameStateController:handleNotification(notification)
    local noticeID = notification:getName()
    local notices  = global.NoticeTable
    local data     = notification:getBody()

    if notices.ChangeGameState == noticeID then
        self:ChangeGameState( data )
    elseif notices.ReleaseMemory == noticeID then
        self:Cleanup()
    end
end
function GameStateController:Init()
    local mmo = global.MMO
    self._gameStates  = {}
    self._currStateID = nil
    self._currState   = nil
    self._gameStates[mmo.GAME_STATE_ACTIVE] = gameStateActive.new()
    self._gameStates[mmo.GAME_STATE_INIT]   = gameStateInit.new()
    self._gameStates[mmo.GAME_STATE_LOGIN]  = gameStateLogin.new()
    self._gameStates[mmo.GAME_STATE_ROLE]   = gameStateRole.new()
    self._gameStates[mmo.GAME_STATE_WORLD]  = gameStateWorld.new()
end

function GameStateController:Cleanup()
    if self._currState then
        print( self._currStateID, "Cleanup" )
        self._currState:Cleanup()
    end
end

function GameStateController:ChangeGameState( newStateID )
    if newStateID == self._currStateID then
        return
    end
    local newState = self._gameStates[newStateID]
    if nil == newState then
        return
    end
    local lastState = self._currState
    if self._currState then
        self._currState:onExit()
    end

    self._currState = newState
    self._currStateID = newStateID

    if lastState then
        print( lastState:getStateType(), "-->", self._currState:getStateType() )
    else
        print( "none -->", self._currState:getStateType() )
    end

    newState:onEnter()
end

function GameStateController:ChangeAppStateCB( appState )
    if 0 == appState then
        self:OnGameSuspend()

    elseif 1 == appState then
        self:OnGameResumed()

    end
end

function GameStateController:OnGameSuspend()
    self:getFacade():sendNotification( global.NoticeTable.GameSuspend )
end

function GameStateController:OnGameResumed()
    self:getFacade():sendNotification( global.NoticeTable.GameResumed )
end

function GameStateController:GetCurrentState()
    return self._currStateID
end


function GameStateController:onRegister()
    GameStateController.super.onRegister( self )

    -- stateMgr
    if not global.gameStateCtl then
        global.gameStateCtl = gameStateMgr:Inst()
    end
    global.gameStateCtl:RegisterLuaHandler( handler( self, self.ChangeAppStateCB ) )
end
function GET_GAME_STATE()
    local stateCtl = global.Facade:retrieveMediator( GameStateController.NAME )
    return stateCtl:GetCurrentState()
end

return GameStateController

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值