天天看點

lua版pureMVC架構使用分析

  一.簡介:今天在網上找架構時,發現了一個使用lua實作的開源PureMVC架構,于是便分析了一下使用方法,以便在自己的項目中可以嘗試使用。

  二.作者的架構提供的案例分析:

    1.Main函數

require 'PureMVC/PureMVC'
require 'StartupMCmd'
require 'AppFacade'
require 'Network'

--遊戲lua入口函數
function Main()
    print('start Main ...')
    AppFacade:GetInstance():Startup()
end      

    Main函數是遊戲項目的入口函數,其中首先執行了一些必要的lua腳本,包括MVC架構、啟動遊戲Command腳本、管理Facade腳本、網絡NetWord腳本。在C#端的代碼中,是直接使用xlua的luaenv執行了require 'Main' Main()語句,也就是執行了Main腳本中的Main函數,啟動了遊戲。

    2.Facade函數

AppFacade = class('AppFacade',PureMVC.Facade)
AppFacade.APP_STARTUP = 'APP_STARTUP'
local module = AppFacade

--ctor等同于class的構造方法,在new()時會被調用。
function module:ctor()
    self.super:ctor()
    --注冊啟動指令StartupMCmd,這是個宏指令。
    --注冊為一個函數原因是實作指令的無狀态性,需要時才被執行個體化出來,省記憶體。
    self:RegisterCommand(AppFacade.APP_STARTUP, function() return StartupMCmd.new() end)
end

--發出啟動遊戲指令。
function module:Startup()
    self:SendNotification(AppFacade.APP_STARTUP)
end      

    在Main函數中啟動了Startup函數,這個函數是Facade中定義的函數。架構定義了一個class方法用于建立類對象(在lua中對象本質上是一張表),第一個參數為對象名,第二個參數為對象繼承的類(lua中本質上是元表),這裡這個對象是繼承自Facade類的;第二行代碼在對象裡添加了一個字元串變量,用于作為名稱和實際的指令綁定。在ctor構造方法中使用RegisterCommand方法進行指令注冊。這裡最後提供的Startup方法就是Main腳本中調用的方法,負責發出啟動指令的消息。

    3.MacroCommand指令

require 'Login/LoginInitCmd'
require 'PlayerInfo/PlayerInfoInitCmd'

StartupMCmd = class('StartupMCmd', PureMVC.MacroCommand)

--這個類以MCmd字尾結尾是一個MacroCommand,
--内部添加了多個子Cmd,按添加順序執行,
--宏Cmd一次性使用,執行完之後子Cmd就會被清空。
function StartupMCmd:InitializeMacroCommand()
    --添加2個UI系統子產品的初始化Cmd
    self:AddSubCommand(function() return LoginInitCmd.new() end)
    self:AddSubCommand(function() return PlayerInfoInitCmd.new() end)
end      

    Macro複合指令和C#版本的puremvc使用是基本一樣的,在其中的InitializeMacroCommand方法中使用AddSubCommand方法添加子指令。值得注意的是在定義指令前,需要執行相應的指令腳本。

    4.SimpleCommand指令

require 'Login/LoginPanel'
require 'Login/LoginMediator'
require 'Login/LoginClickCmd'

LoginInitCmd = class('LoginInitCmd', PureMVC.SimpleCommand)

--Login功能子產品初始化cmd,
--這裡是有view 的Mediator執行個體化,model的Proxy參考PlayerInfo系統子產品
function LoginInitCmd:Execute(notification)
    local panel = LoginPanel.new(LoginPanel.NAME)
    local mediator = LoginMediator.new(LoginPanel.NAME, panel)
    AppFacade:GetInstance():RegisterMediator(mediator)
    AppFacade:GetInstance():RegisterCommand(LoginClickCmd.CMD, function () return LoginClickCmd.new() end)
end      

    Simple指令繼承自SimpleCommand類,在其中的Execute函數中定義指令執行的具體代碼。還是需要注意的是使用代碼時涉及到的相關panel、mediator和command等腳本都需要先執行再使用,執行個體化出來的panel、mediator、proxy等都可以注冊到facade中。

    5.Panel腳本

--以Panel為字尾的類名對應Unity 裡制作的UI預設
--如這裡對應LoginPanel.prefab
--panel類等同于一個view,panel裡隻提供擷取控件的接口,供mediator通路,panel内部不做複雜操作。
LoginPanel = class('LoginPanel')
LoginPanel.NAME = 'LoginPanel'
local module = LoginPanel

module.gameObject = nil
module.transform = nil

module.inputUsername = nil
module.inputPassword = nil
module.btnLogin = nil

function module:ctor(panelName)
    self.panelName = panelName
    self:Load(panelName)
end
--加載unity 裡的LoginPanel.prefab預設,
--并且擷取元件
function module:Load(panelName)
    self.gameObject = CS.ResourcesLoader.Instance:LoadPanel(panelName)
    self.transform = self.gameObject.transform
    self.gameObject:SetActive(false)
    self.inputUsername = self.transform:Find('InputUsername'):GetComponent(typeof(CS.UnityEngine.UI.InputField))
    self.inputPassword = self.transform:Find('InputPassword'):GetComponent(typeof(CS.UnityEngine.UI.InputField))
    self.btnLogin = self.transform:Find('BtnLogin'):GetComponent(typeof(CS.UnityEngine.UI.Button))
end
function module:Show()
    self.gameObject:SetActive(true)
end
function module:Hide()
    self.gameObject:SetActive(false)
end      

    panel就是是MVC中的view,使用mediator去代理它。在panel中持有對應遊戲面闆的相關資訊,如gameObject、transform、各種控件等,在ctor函數中為相關資訊指派,提供函數用于擷取控件,提供顯示隐藏panel的函數等。

    6.mediator中介

--用于對LoginPanel的各種操作,事件處理,資料填充等。
--PureMVC.Mediator同時繼承了Notifier類,是以能夠SendNotification
--PureMVC.Mediator實作了觀察者功能,提供ListNotificationInterests()方法來填寫感興趣的Cmd,并在HandleNotification()回調。
--這說明了Mediator其實也是一個Cmd,以觀察者模式內建了Cmd的功能。
LoginMediator = class('LoginMediator', PureMVC.Mediator)
local module = LoginMediator

function module:ctor(mediatorName, view)
    --覆寫了父類的構造方法,顯示調用父類構造方法,
    --和其它語言有點不同,lua文法特性和class的定義決定了這樣寫
    module.super.ctor(self, mediatorName, view)
    self:init()
end
function module:init()
    self.View:Show()
    self.View.btnLogin.onClick:AddListener(
        function()
           self:onLoginClick() 
        end
    )
end
--點選登入事件
function module:onLoginClick()
    if self.View.inputUsername == nil then
        print(debug.traceback())
        print(self.View.__cname)
    end
    local username = self.View.inputUsername.text
    local password = self.View.inputPassword.text
    local body = {
        username = username,
        password = password
    }
    self:SendNotification(LoginClickCmd.CMD, body)
end
--指定方法ListNotificationInterests()中填寫感興趣的Cmd
function module:ListNotificationInterests()
    return {
        Network.CMD_LOGIN_SUCCESS
    }
end
--感興趣的Cmd 在HandleNotification(notification)中收到回調
function module:HandleNotification(notification)
    if notification.Name == Network.CMD_LOGIN_SUCCESS then
        self.View:Hide()
    end
end      

    在Mediator中介中提供相應panel的各種處理方法,以消息事件的形式提供給外部調用。mediator中介都繼承自Mediator類,ctor構造方法也和C#版本的puremvc一緻,在其中進行控件的方法注冊等操作,view是持有的具體panel,在ListNotificationInterests中傳回mediator關注的事件名稱,在HandleNotification中進行處理。

    7.proxy代理

--Proxy繼承了Notifer接口,可以發送指令SendNotification(),
--但不監聽任何指令。
PlayerInfoProxy = class('PlayerInfoProxy', PureMVC.Proxy)
PlayerInfoProxy.CMD_REFRESH = 'PlayerInfoProxy'
local module = PlayerInfoProxy

function module:GetData()
    return self.Data
end
--提供修改資料的方法,隻應該提供給伺服器資料來時修改用。
function module:Refresh(data)
    self.Data = data
    self:SendNotification(PlayerInfoProxy.CMD_REFRESH)
end      

繼續閱讀