PureMVC框架

PureMVC框架讲解

1. 理论基础

Pure MVC是在基于模型、视图和控制器MVC模式建立的一个轻量级的应用框架,这个框架是开源免费的,目前支持18中开发语言。

  1. 应用PureMVC主要目的就是“解耦”,除了层之间的解耦外,我们也可 以在层内,进行进一步的划分。
  2. 模型层一般都分为:“数据实体类(值对象)”与“数据代理类” 好处是,“数据本身”与“数据操作”相分离,易于存储、扩展等,耦合性降低与扩展性提高。
  3. 视图层也可以基于降低“层内”的耦合性的思想分为:“显示View” 与“显示控制 Mediator”,View : 只负责简单的窗体数据显示;Mediator:负责显示层的通讯、按钮点击事件、数据处理。

官网:http://puremvc.org
github地址:https://github.com/PureMVC

1.1 PureMVC整体架构

官方整体架构图.png
  • Model 与 Proxy
    Model保存对Proxy对象的引用,Proxy负责操作数据模型,存取数据。这样保证了Model层的可移植性。
  • View 与 Mediator
    View保存对Mediator对象的引用。由Mediator对象来操作具体的视图组件(View Component),包括:添加事件监听器,发送或接收Notification ,直接改变视图组件的状态。这样做实现了把视图和控制它的逻辑分离开来。
  • Controller 与 Command
    Controller保存所有Command的映射。Command类是无状态的,只在需要时才被创建。
    Command可以获取Proxy对象并与之交互,发送Notification,执行其他的Command。经常用于复杂的或系统范围的操作,如应用程序的“启动”和“关闭”。应用程序的业务逻辑应该在这里实现。
  • Facade 与 Model,View和Controller
    Facade类应用单例模式,它负责初始化核心层(Model,View和Controller),并能访问它们的Public方法。在实际的应用中,只需继承Facade类创建一个具体的Facade类就可以实现整个MVC模式,并不需要在代码中导入编写Model,View和Controller类。
    Proxy、Mediator和Command就可以通过创建的Facade类来相互访问通信。

1.2 设计模式

  1. 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  2. 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

  3. 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

迪米特法则(Law of Demeter,简称LoD)也叫最小知识原则(Least Knowledge Principle 简写LKP)如果俩个类不必直接通信,那么这俩类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

  1. 命令模式(Command):讲一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

  2. 外观模式(Facade):为了系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  3. 观察者模式(Publish/Subscribe):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

1.3 数据通信

数据通信.png

2. 一个简单的案例

2.1 PureMVC架构基本的开发流程:

  1. 定义自己的ApplicationFacade类(继承Facade)
    1)注册控制层类,以及定义“消息名称”与命令类的绑定关系
    2)注册模型层类
    3)注册视图层类
  2. 定义自己的类
    1)模型层类(继承Proxy)
    2)视图层类(继承Mediator)
    3)控制层类 (继承SimpleCommand或者MacroCommand)
  3. 确定层之间的消息传递。
  4. 调试项目。

2.2 案例介绍

demo地址:https://gitee.com/ZhaoYongshuang/puremvc_framework_demo

简单案例.png

2.3 代码实现

2.3.1 ApplicationFacade.cs

using PureMVC.Patterns;
using PureMVC.Interfaces;

public class ApplicationFacade :Facade
{
    public new static IFacade Instance
    {
        get
        {
            if (m_instance == null)
            {
                lock (m_staticSyncRoot)
                {
                    if (m_instance == null)
                    {
                        m_instance = new ApplicationFacade();
                    }
                }
            }
            return m_instance;
        }
    }
    protected override void InitializeModel()
    {
        base.InitializeModel();
        RegisterProxy(new MyProxy());
    }
    protected override void InitializeView()
    {
        base.InitializeView();
        RegisterMediator(new MyMediator());
    }
    protected override void InitializeController()
    {
        base.InitializeController();
        RegisterCommand("Cmd_StartUpApp", typeof(CmdStartUpApp));
        RegisterCommand("Cmd_AddCount", typeof(CmdAddCount));
    }
}

2.3.2 MyUI.cs

using System;
using UnityEngine;
using UnityEngine.UI;

public class MyUI : MonoBehaviour
{
    private Text txtLevel;
    private Button btnAddCount;

    public Action actionBtnCount;
    void Start()
    {
        txtLevel = transform.Find("TxtCount").gameObject.GetComponent<Text>();
        btnAddCount = transform.Find("BtnCount").gameObject.GetComponent<Button>();
        btnAddCount.onClick.AddListener(OnClickAddCount);
    }
    private void OnClickAddCount()
    {
        actionBtnCount?.Invoke();
    }
    public void SetTxtCount(MyData myData)
    {
        txtLevel.text = myData.Count.ToString();
    }
}

2.3.3 StartGame.cs

using UnityEngine;

public class StartGame : MonoBehaviour {
    public MyUI myUI;

    void Start()
    {
        ApplicationFacade appFacade = ApplicationFacade.Instance as ApplicationFacade;
        if (appFacade != null && myUI != null)
        {
            appFacade.SendNotification("Cmd_StartUpApp", myUI);
        }
    }
}

2.3.4 CmdStartUpApp.cs

using PureMVC.Interfaces;
using PureMVC.Patterns;

public class CmdStartUpApp : SimpleCommand
{
    public override void Execute(INotification notification)
    {
        MyUI myUI = notification.Body as MyUI;
        if(myUI != null)
        {
            SendNotification("Msg_InitMyUI",myUI); 
        }
    }
}

2.3.5 MyMediator.cs

using System.Collections.Generic;
using PureMVC.Interfaces;
using PureMVC.Patterns;

public class MyMediator : Mediator {
    public new const string NAME = "MyMediator";

    private MyUI myUI
    {
        get
        {
            return base.ViewComponent as MyUI;
        }
    }
    public override IList<string> ListNotificationInterests()
        {
            IList<string> listResult=new List<string>();
        listResult.Add("Msg_InitMyUI");
        listResult.Add("Msg_AddCount");
            return listResult;
        }
        public override void HandleNotification(INotification notification)
        {
        switch (notification.Name)
        {
            case "Msg_InitMyUI":
                MyUI myUI = notification.Body as MyUI;
                this.InitMyUIMediator(myUI);
                break;
            case "Msg_AddCount":
                MyData myData = notification.Body as MyData;
                this.AddCount(myData);
                break;
            default:
                break;
        }
        }
    private void InitMyUIMediator(MyUI myUI)
    {
        if (myUI != null)
        {
            base.m_mediatorName = NAME;
            base.m_viewComponent = myUI;
            myUI.actionBtnCount = this.OnClickAddCount;
        }
    }
    private void OnClickAddCount()
    {
        SendNotification("Cmd_AddCount");
    }
    private void AddCount(MyData myData)
    {
        this.myUI.SetTxtCount(myData);
    }
}

2.3.6 CmdAddCount.cs

using PureMVC.Interfaces;
using PureMVC.Patterns;

public class CmdAddCount :SimpleCommand {
    public override void Execute(INotification notification)
    {
        MyProxy myProxy = (MyProxy)Facade.RetrieveProxy(MyProxy.NAME);
        myProxy.AddLevel(10);
    }
}

2.3.7 MyData.cs

public class MyData
{
    private int _count = 0;

    public int Count
    {
        get { return _count; }
        set { _count = value; }
    }
}

2.3.8 MyProxy.cs

using PureMVC.Patterns;

public class MyProxy :Proxy {
    public new const string NAME = "MyProxy";
        private MyData myData = null;

        public MyProxy():base(NAME)
        {
        myData = new MyData();
        }
    public void AddLevel(int addNumber)
    {
        myData.Count += addNumber;
        SendNotification("Msg_AddCount", myData);
    }
}

2.4 消息流程

案例消息流程图.png

3 框架源码分析

源码在上面的demo中有,可自行下载查看

3.1 源码文件结构

源码文件.png

3.2 源码消息流程

源码消息流程.png
  • Controller保存了Command与Notification之间的映射。当Notification(通知)被发出时,对应的Command(命令)就会自动地由Controller执行。Command实现复杂的交互,降低View和Model之间的耦合度。
  • Mediator发送、声明、接收Notification。当用View注册Mediator时,Mediator的ListNotificationInterests方法会被调用,以list形式返回该Mediator对象所关心的所有Notification。之后,当系统其它角色发出同名的Notification(通知)时,关心这个通知的Mediator都会调用handleNotification方法并将Notification以参数传递到方法。
  • Proxy发送,但不接收Notification。在很多场合下Proxy需要发送Notification(通知),比如:Proxy从远程服务器接收到数据时,发送Notification告诉系统;或当Proxy的数据被更新时,发送Notification告诉系统。

3.3 模块图

PureMVC源码整体架构图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348