当你开发一个客户端应用程序的时候,往往一个单页会包含很多子模块,在不同的平台下,这些子模块又被叫成子View(视图),或者子Component(组件)。越是复杂的页面,被切割出来的子模块就越多,子模块越多,彼此之间需要同步的数据和状态就越频繁,即易产生耦合。那么如何保证在复杂业务情况下,各个子模块之间可以随意通信并保持弱耦合关系,这正是本文所讨论的。

耦合的产生

试想一下,你有这样一下需求,点击 View A中的按钮,View B也需要做出相应的改变。

这不是很简单吗。脑海里迅速出现两种解决方案:

1.View A 主动通知View B做出更新,也就是View A依赖 View B

void Notify()
{
ViewB.Update(color);
}

2.View B监听View A的ColorChanged事件,主动拉取数据并更新,即ViewB 依赖View A

ViewA.OnColorPropertyValueChanged+=(color)=>{
Update(color);**
}

这两种实现毫无疑问是没问题的,至少从结果上来看是正确的。但试想一下,在一个复杂的客户端单页应用程序,这种紧耦合关系会导致程序的复杂度陡然上升。每个View/ViewModel依赖其余对象,而本身又被其他View/ViewModel强引用。这显然不是好的实践方式。

还记得我在上一篇文章的对于MVVM的描述吗?

MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在。ViewModel与ViewModel之间也应该感受不到彼此的存在。

中介者模式的引入

那么如何消除这种紧耦合关系呢?交给中介者设计模式来解决吧。

我们需要添加一个中介者,每个ViewModel Publisher对象都会在自己状态改变时,告诉中介者。每个ViewModel Subscribers 都需要告诉中介者请求来时进行怎样的响应。

在没有中介者之前对象之间都需要彼此认识,互相引用,是一种强耦合关系。有了中介者之后,彻底解耦。

那么现在就需要定义一个中介者,称为MessageAggregator。因为由它来转发消息,所以核心是一个字典,保存了所有需要被转发的消息。它的Key为消息的唯一Id,Value代表一个对该Message的处理程序。

public delegate void MessageHandler<T>(object sender, MessageArgs<T> args);
public class MessageAggregator<T>
{
private readonly Dictionary<string, MessageHandler<T>> _messages = new Dictionary<string, MessageHandler<T>>(); public static readonly MessageAggregator<T> Instance=new MessageAggregator<T>(); private MessageAggregator()
{ } public void Subscribe(string name, MessageHandler<T> handler)
{
if (!_messages.ContainsKey(name))
{
_messages.Add(name, handler);
}
else
{
_messages[name] += handler;
} }
public void Publish(string name, object sender, MessageArgs<T> args)
{
if (_messages.ContainsKey(name) && _messages[name] != null)
{
//转发
_messages[name](sender, args);
}
} }

解耦ViewModel与ViewModel###

通过中介者MessageAggregator对象,ViewModelB Subscribe一个对消息来时的处理函数:

MessageAggregator<object>.Instance.Subscribe("ColorChanged",ToggleHandler);

ViewModel A在自己状态改变时,Pulish状态改变的消息给中介者:

MessageAggregator<object>.Instance.Publish("ColorChanged", this,new MessageArgs<object>("Red"));

小结###

中介者模式常常用来协调相关的GUI组件,可以让对象之间传递的消息变得简单。但如果设计不当,中介者本身会变得过于复杂。

源代码托管在Github上,点击此了解

Unity 3D Framework Designing(2)——使用中介者模式解耦ViewModel之间通信的更多相关文章

  1. Unity应用架构设计(2)——使用中介者模式解耦ViewModel之间通信

    当你开发一个客户端应用程序的时候,往往一个单页会包含很多子模块,在不同的平台下,这些子模块又被叫成子View(视图),或者子Component(组件).越是复杂的页面,被切割出来的子模块就越多,子模块 ...

  2. Unity 3D Framework Designing(3)——构建View和ViewModel的生命周期

    > 对于一个View而言,本质上是一个MonoBehaviour.它本身就具备生命周期这个概念,比如,Awake,Start,Update,OnDestory等.这些是非常好的方法,可以让开发者 ...

  3. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 2)

    MVVM回顾 经过上一篇文章的介绍,相信你对 MVVM的设计思想有所了解.MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在.View只关心怎样渲染,而ViewModel只 ...

  4. Unity 3D Framework Designing(1)—— MVVM 模式的设计和实施(Part 1)

    初识 MVVM 谈起 MVVM 设计模式,可能第一映像你会想到 WPF/Sliverlight,他们提供了的数据绑定(Data Binding),命令(Command)等功能,这让 MVVM 模式得到 ...

  5. Unity 3D Framework Designing(4)——设计可复用的SubView和SubViewModel(Part 1)

    『可复用』这个词相信大家都熟悉,通过『可复用』的组件,可以大大提高软件开发效率. 值得注意的事,当我们设计一个可复用的面向对象组件时,需要保证其独立性,也就是我们熟知的『高内聚,低耦合』原则. 组件化 ...

  6. Unity 3D Framework Designing(6)——设计动态数据集合ObservableList

    什么是 『动态数据集合』 ?简而言之,就是当集合添加.删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面.有经验的程序员脑海里迸出的第一个词就是 ObservableCollection.没 ...

  7. Unity 3D Framework Designing(7)——IoC工厂理念先行

    一谈到 『IoC』,有经验的程序员马上会联想到控制反转,将创建对象的责任反转给工厂.IoC是依赖注入 『DI』 的核心,大名鼎鼎的Spring框架就是一个非常卓越的的控制反转.依赖注入框架.遗憾的是, ...

  8. Unity 3D Framework Designing(9)——构建统一的 Repository

    谈到 『Repository』 仓储模式,第一映像就是封装了对数据的访问和持久化.Repository 模式的理念核心是定义了一个规范,即接口『Interface』,在这个规范里面定义了访问以及持久化 ...

  9. Unity 3D Framework Designing(4)——设计可复用的SubView和SubViewModel(Part 2)

    在我们设计和开发应用程序时,经常要用到控件.比如开发一个客户端WinForm应用程序时,微软就为我们提供了若干控件,这些控件为我们提供了可被定制的属性和事件.属性可以更改它的外观,比如背景色,标题等, ...

随机推荐

  1. 使用JAVA开发微信公众平台(一)——环境搭建与开发接入

    一. 初始微信公众平台 微信公众平台,即我们平时所说的"公众号",曾用名"官方平台"."媒体平台",但最终命名为"公众平台&quo ...

  2. vue自定义日期组件

    vue-datepicker 基于 vuejs 2.x 可自定义主题的日期组件 github Usage 只需要在项目中加入 calendar.vue,就可以使用了. 向组件传入 prop 即可改变 ...

  3. Java多线程程序休眠、暂停与停止

    休眠   在Java多线程中,可以使用sleep()方法在指定毫秒数内让当前正在执行的线程休眠.   下面这段代码,使得主函数的main线程休眠了2000ms,最后输出的间隔时间也是2000ms. p ...

  4. Sass与Compass——回顾

    compass 是sass的一个工具库 compass在sass 的基础上封装了一系列有用的模块,用来补充和丰富sass的工能, 安装: compass是用 ruby语言开发的,所以安装它之前必须安装 ...

  5. 深入理解javaScript的深复制和浅复制

    javascript有五种基本数据类型(也就是简单数据类型),它们分别是:Undefined,Null,Boolean,Number和String.还含有一种复杂数据类型,就是对象 注意Undefin ...

  6. C#中字符和字符串总结

    Char类是C#提供的字符类型,String是C#提供的字符串类型. 字符: Char类在C#中表示一个Unicode字符. Char类只定义一个Unicode字符. Char类常用的方法及说明如下: ...

  7. 前端总结·基础篇·CSS(一)布局

    目录 这是<前端总结·基础篇·CSS>系列的第一篇,主要总结一下布局的基础知识. 一.显示(display) 1.1 盒模型(box-model) 1.2 行内元素(inline) &am ...

  8. 【异构计算】在Windows下使用OpenCL配置

    前言 目前,NVIDIA 和 AMD 的 Windows driver 均有支持OpenCL(NVIDIA 的正式版 driver 是从自195.62 版开始,而 AMD则是从9.11 版开始).NV ...

  9. mac环境下mentohust锐捷登录配置

    今天测试react native嵌入原生项目,账号流量恰好用完,想换同学账号却不会更改配置,以至于被网络弄得头疼了一中午.于是,好好研究了一下这个mentohust 一.需要下载的文件 我已经全部整理 ...

  10. 1详细解析HTML基础结构

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...