在我们设计和开发应用程序时,经常要用到控件。比如开发一个客户端WinForm应用程序时,微软就为我们提供了若干控件,这些控件为我们提供了可被定制的属性和事件。属性可以更改它的外观,比如背景色,标题等,而事件可以丰富控件的行为,比如最常见的『按钮点击』,谁也不能确定点击之后将发生什么事,是连接数据库呢还是弹出警告框,在不同的场景下,『按钮点击』 的行为往往呈现不一致。所以,与其举棋不定,还不如把处理委托给开发者,这就是『OnClick』事件。

SubView行为多变性

在上篇文章中,我阐述了为什么要使用SubView,总结起来就3个字:『可复用』 。那么问题来了,既然是可复用,那就意味着SubView可以在任何场景下使用,那怎样才能确保它做的是正确的行为呢?

举个栗子,还是 以如下图FaceBox为例,不同的场景下点击头像应该处理不同的事:

  • 在战团中点击头像,则显示该成员的具体信息
  • 在队伍里点击头像,则进入换人界面
  • 在战斗时点击头像,则显示它配置的战术

你看,同样一个SubView,在不同的场景下它的行为往往是不一致的。那我们怎么去跟踪这些行为呢?

定制SubView的行为

你可能会以如下方式去定制SubView的行为:

  1. void OnClick(){
  2. if(战团){
  3. 显示该成员的具体信息
  4. }else if(队伍){
  5. 进入换人界面
  6. }else if(战斗){
  7. 显示它配置的战术
  8. }else{
  9. //其他
  10. }
  11. }

还是那句话这样,这样并没有错,甚至对某些SubView而言逻辑还很清晰。但仔细想想,这是最好的实践吗?

  • 如果我要继续添加一种情况,是不是只能在else if扩展,违反了开闭原则,应该对扩展是开放的,对修改是关闭的
  • 既然这个SubView是可复用的,那意味着将它放在任何项目中都是没问题的,但实际上OnClick里面处理了业务逻辑,紧耦合当前游戏的业务

所以显然上述代码不是最佳实践。那我们应该怎样去解决呢?

实际从开头的引言我已经提出了解决方案,以事件的形式委托给开发者来确定。一个Button也好,还是一个SubView也好,他们都是可复用的组件,不应该与具体的业务逻辑相结合。通过事件或者委托的形式,暴露给开发者来决定究竟要处理什么逻辑,这样才能和具体业务逻辑解耦。

委托的介入

还是以FaceBox举例,那么从上面的分析得出结论,我们需要定义委托或者事件,那应该定义在FaceBoxView呢还是FaceBoxViewModel中呢?

还是那句话,View不处理具体的业务逻辑,View将请求交给ViewModel去处理。

故在FaceBoxViewModel中增加可被外界监听的委托或者事件,我以委托举例,实际上事件就是特殊的委托。

  1. public class FaceBoxViewModel:ViewModelBase
  2. {
  3. //省略部分代码
  4. public delegate void OnBeginDragHandler();
  5. public OnBeginDragHandler OnBeginDrag;
  6. public delegate void OnDragHandler();
  7. public OnDragHandler OnDrag;
  8. public delegate void OnEndDragHandler();
  9. public OnEndDragHandler OnEndDrag;
  10. public delegate void OnClickHandler();
  11. public OnClickHandler OnClick;
  12. //省略部分代码
  13. }

FaceBoxView不处理具体的逻辑,交由FaceBoxViewModel去实现:

  1. protected override void OnInitialize()
  2. {
  3. //省略部分代码
  4. //监听事件
  5. var beginDragEntry = new EventTrigger.Entry();
  6. beginDragEntry.eventID = EventTriggerType.BeginDrag;
  7. beginDragEntry.callback.AddListener(eventData => { OnBeginDrag(); });
  8. eventTrigger.triggers.Add(beginDragEntry);
  9. var dragEntry = new EventTrigger.Entry();
  10. dragEntry.eventID = EventTriggerType.Drag;
  11. dragEntry.callback.AddListener(eventData => { OnDrag(); });
  12. eventTrigger.triggers.Add(dragEntry);
  13. var endDragEntry = new EventTrigger.Entry();
  14. endDragEntry.eventID = EventTriggerType.EndDrag;
  15. endDragEntry.callback.AddListener(eventData => { OnEndDrag(); });
  16. eventTrigger.triggers.Add(endDragEntry);
  17. var pointClickEntry = new EventTrigger.Entry();
  18. pointClickEntry.eventID = EventTriggerType.PointerClick;
  19. pointClickEntry.callback.AddListener(eventData => { OnClick(); });
  20. eventTrigger.triggers.Add(pointClickEntry);
  21. }
  22. private void OnClick()
  23. {
  24. if (BindingContext.OnClick != null)
  25. {
  26. BindingContext.OnClick();
  27. }
  28. }

脑海里梳理一下请求的流程:FaceBoxView.PointClick->FaceBoxViewModel.OnClick()->委托给外部的某个Handler。

小结

实际上『委托』这个概念非常重要,和具体的语言、平台无关。比如在iOS开发经常听到代理模式,顾名思义,将请求交给具体的处理者去处理。设计模式并不深奥,很多模式的理念都是相通的,不同的是对应语言下不同的表现形态,善于剖开现象看本质,很多都是相通的。

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

Unity应用架构设计(4)——设计可复用的SubView和SubViewModel(Part 2)的更多相关文章

  1. Unity应用架构设计(4)——设计可复用的SubView和SubViewModel(Part 1)

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

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

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

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

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

  4. 认证鉴权与API权限控制在微服务架构中的设计与实现(四)

    引言: 本文系<认证鉴权与API权限控制在微服务架构中的设计与实现>系列的完结篇,前面三篇已经将认证鉴权与API权限控制的流程和主要细节讲解完.本文比较长,对这个系列进行收尾,主要内容包括 ...

  5. atitit.系统架构图 的设计 与工具 attilax总结

    atitit.系统架构图 的设计 与工具 attilax总结 1. 架构图的4个版式(标准,(左右)悬挂1 2. 架构图的层次结构(下属,同事,助手)1 3. wps ppt1 4. 使用EDraw画 ...

  6. Java生鲜电商平台-App系统架构开发与设计

    Java生鲜电商平台-App系统架构开发与设计 说明:阅读此文,你可以学习到以下的技术分享 1.Java生鲜电商平台-App架构设计经验谈:接口的设计2.Java生鲜电商平台-App架构设计经验谈:技 ...

  7. Java生鲜电商平台-商品基础业务架构设计-商品设计

    Java生鲜电商平台-商品基础业务架构设计-商品设计 在生鲜电商的商品中心,在电子商务公司一般是后台管理商品的地方.在前端而言,是商家为了展示商品信息给用户的地方,它是承担了商品的数据,订单,营销活动 ...

  8. zz《分布式服务架构 原理、设计与实战》综合

    这书以分布式微服务系统为主线,讲解了微服务架构设计.分布式一致性.性能优化等内容,并介绍了与微服务系统紧密联系的日志系统.全局调用链.容器化等. 还是一样,每一章摘抄一些自己觉得有用的内容,归纳整理, ...

  9. ABSD 基于架构的软件设计方法方法简介(摘抄)

    ABSD(Architecture-Based Software Design)基于架构的软件设计方法 有三个基础: 第一个基础是功能分解.在功能分解中,ABSD方法使用已有的基于模块的内聚和耦合技术 ...

随机推荐

  1. Django2.0引入css、js、img文件

    Django2.0引入css.js.img文件 一.文件结构 二.settings.py的配置 # Static files (CSS, JavaScript, Images) # https://d ...

  2. vue cli3.0 结合echarts3.0和地图的使用方法

    echarts 提供了直观,交互丰富,可高度个性化定制的数据可视化图表.而vue更合适操纵数据. 最近一直忙着搬家,就没有更新博客,今天抽出空来写一篇关于vue和echarts的博客.下面是结合地图的 ...

  3. JDK 1.6 HashMap 源码分析

    前言 ​ 前段时间研究了一下JDK 1.6 的 HashMap 源码,把部份重要的方法分析一下,当然HashMap中还有一些值得研究得就交给读者了,如有不正确之处还望留言指正. 准备 ​ 需要熟悉数组 ...

  4. Java 的Event机制浅析

    https://blog.csdn.net/kehyuanyu/article/details/23540901

  5. 服务端spark gbdt模型计算性能优化

    服务端使用训练出来的模型,spark模型计算第一步是实现spark模型加载. 线上服务对用户体验影响极大,故需要对模型使用进行优化. 1.多线程并发进行计算,线上两个服务.优化cpu 2.在扩召回集, ...

  6. Kotlin基础(四)Lambda编程

    Lambda编程 一.Lambda表达式和成员引用 一)Lambda表达式语法 //注意与Java8中的区别 val sum={ x:Int,y:Int -> x+y } println(sum ...

  7. django rest_framework 序列化组件详解

    为什么要用序列化组件 当我们做前后端分离的项目,我们前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式. 那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到 ...

  8. js中使用将json数组 转换为json将一个包含对象的数组用以创建一个包含新重新格式化对象的新数组

    1.使用reduce: let arr = [{ "code": "badge", "priceList": [{ "amount ...

  9. 如何使用单例模式返回一个安全的Connection

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public clas ...

  10. Flask框架返回值

    Flask中的HTTPResponse def index(): #视图函数 return 'Hello World' #直接return就是返回的字符串 Flask中的Redirect,和djang ...