Unity应用架构设计(1)—— MVVM 模式的设计和实施(Part 2)

MVVM回顾

经过上一篇文章的介绍,相信你对MVVM的设计思想有所了解。MVVM的核心思想就是解耦,View与ViewModel应该感受不到彼此的存在。
View只关心怎样渲染,而ViewModel只关心怎么处理逻辑,整个架构由数据进行驱动。不仅View与ViewModel彼此解耦,ViewModel与ViewModel之间也是解耦的。
通过消息订阅-发布机制,解决了ViewModel之间的强依赖关系。

先回顾一下我们已完成的功能,Framework中最核心就是BindableProperty 类,ViewModel 中所有需要被绑定到UI 控件的属性必须是一个BindableProperty 对象。它是一个职责非常单一的类,监听Value的数值是否发生变化,当变化时,触发OnValueChanged 事件,通知View 做出相应的更新。

public class BindableProperty<T>
{public delegate void ValueChangedHandler(T oldValue, T newValue);public ValueChangedHandler OnValueChanged;private T _value;public T Value{get{return _value;}set{if (!object.Equals(_value, value)){T old = _value;_value = value;ValueChanged(old, _value);}}}private void ValueChanged(T oldValue, T newValue){if (OnValueChanged != null){OnValueChanged(oldValue, newValue);}}
}

那问题来了,View在何时并以怎样的方式去监听这些属性的变化呢?

BindableProperty是一个很好的设计,它不仅可以用在ViewModel中,还可以用在View中,用它来修饰 ViewModel,当ViewModel 改变时,比如初始化时,或者从一个ViewModel变化到另一个ViewModel对象时,在触发的OnBindingContextChanged 事件中实现对ViewModel中的属性监听。如下定义的抽象父类:UnityGuiView

public readonly BindableProperty<ViewModel> ViewModelProperty = new BindableProperty<ViewModel>();
public ViewModel BindingContext
{get { return ViewModelProperty.Value; }set { ViewModelProperty.Value = value; }
}protected virtual void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
{
}public UnityGuiView()
{this.ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}

子类SetupView继承自UnityGuiView,并且Override OnBindingContextChanged,并实现对ViewModel中的属性监听。

protected override void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
{base.OnBindingContextChanged(oldViewModel, newViewModel);SetupViewModel oldVm = oldViewModel as SetupViewModel;if (oldVm != null){oldVm.Name.OnValueChanged -= NameValueChanged;...}if (ViewModel!=null){ViewModel.Name.OnValueChanged += NameValueChanged;...}
}

进一步抽象

实际上对于ViewModel而言会有非常多的BindableProperty需要被绑定到UI控件中,从代码的可读性而言,如下代码是非常沉长和啰嗦的:

if (oldVm != null)
{oldVm.Name.OnValueChanged -= NameValueChanged;...
}
if (ViewModel!=null)
{ViewModel.Name.OnValueChanged += NameValueChanged;...
}

因为+=和-=是成对出现的,所以只要是看到 OnValueChanged,这部份代码的长度几乎都是*2。

仔细观察一下,每个View都会出现 具体的 ViewModel.属性.OnValueChanged事件+=或者-=具体的处理函数 这样的固定模板。
那么是否可以将这部分代码抽象到一个公共类中呢,并且暴露出一个简单的方法提供给View来初始化这些OnValueChanged事件,比如:

PropertyBindingUtils.Init<string>("Color",OnColorPropertyValueChanged);

然后在Init方法中+=或者-=具体的处理函数。

当然是可以得,定义一个PropertyBinder属性绑定器,通过反射技术,动态为属性+=或者-= OnValueChanged 事件,脑海里的 Raw 代码如下

Init<TProperty>(string propertyName ,OnValueChanged valueChangedHandler)
{var fieldInfo = typeof(TViewModel).GetField(propertyName, BindingFlags.Instance | BindingFlags.Public);var value = fieldInfo.GetValue(viewModel);BindableProperty<TProperty> bindableProperty = value as BindableProperty<TProperty>;bindableProperty.OnValueChanged += valueChangedHandler;bindableProperty.OnValueChanged -= valueChangedHandler;
} 

最核心的代码就那么几步,详细代码可以查看源代码PropertyBinder的实现。

重构视图基类:UnityGuiView

想象一下PropertyBinder应该放在哪儿。

它是用来监听ViewModel中的属性值变化的,用来替换沉长的 oldVm.Property.OnValueChanged +=和-= NameValueChanged,理所应当应该放在View中,因为每个View都需要,故将它定义在UnityGuiView 中。
又因为PropertyBinder需要知道为哪个ViewModel进行服务(因为需要反射),故通过泛型来约束 UnityGuiView< T >:IView where T:ViewModelBase 。

再对BindingContext稍作改变,当它被赋值时,只初始化一次对OnValueChanged事件的监听(原先是放在构造函数里)。

public readonly BindableProperty<ViewModelBase> ViewModelProperty = new BindableProperty<ViewModelBase>();
public ViewModelBase BindingContext
{get { return ViewModelProperty.Value; }set{if (!_isBindingContextInitialized){OnInitialize();_isBindingContextInitialized = true;}//触发OnValueChanged事件ViewModelProperty.Value = value;}
}
/// <summary>
/// 初始化View,当BindingContext改变时执行
/// </summary>
protected virtual void OnInitialize()
{//无所ViewModel的Value怎样变化,只对OnValueChanged事件监听(绑定)一次ViewModelProperty.OnValueChanged += OnBindingContextChanged;
}

值得注意的事,我定义了一个virtual的OnInitialize,这样子类可以override它从而实现一些初始化方法,比如:

protected override void OnInitialize()
{base.OnInitialize();Binder.Add<string>("Color",OnColorPropertyValueChanged);
}private void OnColorPropertyValueChanged(string oldValue, string newValue)
{switch (newValue){case "Red":buttonImage.color = Color.red;break;case "Yellow":buttonImage.color = Color.yellow;break;default:break;}
}

小节

这篇博客基本上是回顾了MVVM模式在Unity 3D上的实践,结合自己的开发经验,通过反射的技术可以有效减少沉长的代码。
源代码托管在Github上,点击此了解

转载于:https://www.cnblogs.com/OceanEyes/p/unity3d_framework_designing_get_started_with_mvvm_part2.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/355954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

apache camel_REST端点,可使用Apache Camel进行集成

apache camelREST是一种用于组织资源的体系结构样式&#xff0c;当应用于基于HTTP的服务时&#xff0c;REST可以构建无状态&#xff0c;分离的可伸缩服务。 HTTP方法&#xff0c;HTTP标头和mime类型都允许开发人员实现REST样式。 诸如Jersey和Fuse Services Framework&#xff…

Linux读取SSD的smart信息,使用smartmontools查看SSD的“秘密”信息

仍然担心看不到sm841中的温度吗&#xff1f;您是否仍对Toshiba Q pro看不见写入量和使用寿命值感到不安&#xff1f;为了查看M4 / 00的写入量&#xff0c;您是否仍在使用C300固件升级程序重新启动到纯DOS并麻烦地运行命令&#xff1f;想知道在协议级别上SSD出了什么问题吗&…

Linux查看系统信息的一些命令及查看已安装软件包的命令

系统 # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat /proc/cpuinfo # 查看CPU信息 # hostname # 查看计算机名 # lspci -tv # 列出所有PCI设备 # lsusb -tv # 列出所…

5条Java记录规则

日志记录是一个关键因素&#xff0c;在软件开发过程中应始终将其考虑在内。 当生产中发生不良情况时&#xff0c;日志文件通常是我们进行故障分析的起点。 而且&#xff0c;通常&#xff0c;它们是我们掌握的唯一信息&#xff0c;可以了解发生了什么以及问题的根本原因。 正确…

linux 离线安装中文字库,centos7 离线安装字体fontconfig

起因&#xff1a;最近做了个flowable然而linux下乱码&#xff0c;发现需要安装字体包在线&#xff1a;直接 yum -y install fontconfig&#xff1b;yum -y install ttmkfdir&#xff1b;配置下即可。拓展&#xff1a;离线所需rpm包如何获取&#xff1f;百度不好找&#xff0c;找…

锁定机制和数据并发管理(笔记)

共享锁和排它锁 排它锁&#xff1a;当某一个会话正在更新某一行&#xff0c;为了防止其他会话修改这一行&#xff0c;这行会被锁定这种锁称为排他锁。被排他锁锁定的行仍然可以被其他会话读取。 共享锁&#xff1a;在一个表上放置共享锁的目的是为了防止其他会话获得这个表上的…

linux防火墙配置说明,Linux防火墙配置命令参数说明

规则操作参数说明&#xff1a;-A&#xff1a;在所选择的链末添加一条或更多规则&#xff1b;-D&#xff1a;从所选链中删除一条或更多规则。有两种方法&#xff1a;把被删除规则指定为链中的序号(第一条序号为1)&#xff0c;或者指定为要匹配的规则&#xff1b;-R&#xff1a;从…

【react.js + hooks】useGuide 创建用户引导视图

有的时候用户可能对网站上的一些操作流程感到困惑&#xff0c;这时候我们需要为用户创建引导视图。为了插入指引而专门去更改组件的渲染函数&#xff0c;显然是不合逻辑的&#xff0c;创建指引视图应该是一种对源代码低侵入的行为&#xff0c;我们可以遵循某一套约定&#xff0…

使用递归算法结合数据库解析成java树形结构

使用递归算法结合数据库解析成java树形结构 1、准备表结构及对应的表数据a、表结构&#xff1a; create table TB_TREE ( CID NUMBER not null, CNAME VARCHAR2(50), PID NUMBER //父节点 ) b、表数据&#xff1a; insert into tb_tree (CID, CNAME, PID) values (1, 中国, 0);…

ug11 linux,UG11.0升级包MP02Win#Linux系统下载就上UG网

UG11.0软件又出升级包啦&#xff01;抽空可以为NX升级啦&#xff0c;从11.0发布到现在&#xff0c;近三个月了&#xff0c;每一次版本的更新&#xff0c;都会带来较多功能的改善&#xff0c;以及对错误BUG的有效处理&#xff0c;下图为升级后的UG11.0软件&#xff1b;UG11.0升级…

akka 异常处理_使用Akka处理1000万条消息

akka 异常处理Akka演员承诺并发。 有什么更好的模拟方法&#xff0c;看看使用商品硬件和软件处理1000万条消息需要花费多少时间&#xff0c;而无需进行任何低级调整。我用Java编写了整个1000万条消息的处理过程&#xff0c;整个结果令我惊讶。 当我在具有i5 – 4核心&#xff0…

20155330 2016-2017-2 《Java程序设计》第五周学习总结

20155330 2016-2017-2 《Java程序设计》第五周学习总结 教材学习内容总结 学习目标 理解异常架构掌握try...catch...finally处理异常的方法会用throw,throws理解Collection和Map架构会用常见的数据结构和算法了解Lambada和泛型第八章 章节主要内容 小结 Throwwable定义了取错误…

yum安装odbc驱动linux,在CentOS上离线配置PostgreSQL ODBC数据源

一、问题提出内网的一台CentOS服务器&#xff0c;需配置PostgreSQL ODBC。如果可以连接Internet&#xff0c;此工作很容易&#xff0c;使用yum install自动安装相应依赖包后简单配置即可。但当置于内网环境时&#xff0c;事情就有些麻烦&#xff0c;需要事先手工下载各个依赖包…

树形dp小胖守皇宫(vijosP1144)

题目链接&#xff1a;https://vijos.org/p/1144 题解&#xff1a;这道题的动归稍稍有一点的复杂&#xff0c;因为一个节点有可能被它的子节点观察&#xff0c;也有可能被父节点观察&#xff1b; 所以我们这样表示&#xff1a; f[i][0]&#xff08;表示当前i节点放了一个看守&am…

微服务和Java EE

基于微服务的架构如今无处不在。 我们对Netflix和Amazon等当今的创新者如何利用它们在成功产生更多业务方面取得更大的成功了解到很多。 但是&#xff0c;我们所有人都在使用Java EE应用程序服务器并编写经典系统吗&#xff1f; 我们都做错了吗&#xff1f; 我们如何使我们的技…

pcie组raid linux,PCIe 4.0有多强大?组RAID 0阵列之后惊呆了

技嘉在6月16日21:00正式上市了全系列B550系列主板&#xff0c;B550系列主板属于AMD中端系列芯片组&#xff0c;能原生支持PCIe 4.0技术&#xff0c;而这次技嘉B550 AORUS MASTER主板更是不得了&#xff0c;提供了3个支持PCIe 4.0技术的M.2接口&#xff0c;你还可以组RAID 0阵列…

oracle中 trunc(),round(),ceil(),floor的使用

原文&#xff1a; http://www.2cto.com/database/201310/248336.html 1.round函数(四舍五入&#xff09; 描述 : 传回一个数值&#xff0c;该数值是按照指定的小数位元数进行四舍五入运算的结果参数: number : 欲处理之数值 decimal_places : 四舍五入 , 小数取几位 ( 预设为 0…

Java中的指针

Java中是否有指针&#xff1f; 简短的答案是“不&#xff0c;没有”&#xff0c;这对于许多开发人员来说似乎是显而易见的。 但是&#xff0c;为什么对其他人却不那么明显呢&#xff1f; http://stackoverflow.com/questions/1750106/how-can-i-use-pointers-in-java http://…

Android实践 -- 监听应用程序的安装、卸载

监听应用程序的安装、卸载 在AndroidManifest.xml中注册一个静态广播,监听安装的广播android.intent.action.PACKAGE_ADDED 监听程序卸载的广播android.intent.action.PACKAGE_REMOVED ,在广播中一定要加上 <data android:scheme"package" /> 不然就监听不到 &…

符合c语言常量12abc,《C语言》试题abc合2006上.doc

《C语言》试题abc合2006上《c语言》试题(A)一、单项选择题(每小题2分&#xff0c;共20分)以下选项中正确的整型常量是 BA)12. B) -20 C) 1,000 D) 4 5 62.以下选项中不合法的用户标识符是 AA)abc.c B)file C)Main D)PRINT3&#xff0e;可在C 程序中用作用户标识符的标识符是 BA…