从MVC框架看MVC架构的设计

从MVC框架看MVC架构的设计尽管MVC早已不是什么新鲜话题了,但是从近些年一些优秀MVC框架的设计上,我们还是会发现MVC在架构设计上的一些新亮点。本文将对传统MVC架构中的一些弊病进行解读,了解一些优秀MVC框架是如何化解这些问题的,揭示其中所折射出的设计思想与设计理念。MVC回顾作为一种经典到不能再经典的架构模式,MVC的成功有其必然的道理,这个道理不同的人会有不同的解读,笔者最认同的一种观

大家好,又见面了,我是你们的朋友全栈君。

推荐:博主历时三年倾注大量心血创作的大数据平台架构与原型实现:数据中台建设实战一书已由知名IT图书品牌电子工业出版社博文视点出版发行,真诚推荐给每一位读者!点击重磅推荐:建大数据平台太难了!给我发个工程原型吧!了解图书详情,扫码进入京东手机购书页面!

从MVC框架看MVC架构的设计

 

从MVC框架看MVC架构的设计

尽管MVC早已不是什么新鲜话题了,但是从近些年一些优秀MVC框架的设计上,我们还是会发现MVC在架构设计上的一些新亮点。本文将对传统MVC架构中的一些弊病进行解读,了解一些优秀MVC框架是如何化解这些问题的,揭示其中所折射出的设计思想与设计理念。

MVC回顾

作为一种经典到不能再经典的架构模式,MVC的成功有其必然的道理,这个道理不同的人会有不同的解读,笔者最认同的一种观点是:通过把职责、性质相近的成分归结在一起,不相近的进行隔离,MVC将系统分解为模型、视图、控制器三部分,每一部分都相对独立,职责单一,在实现过程中可以专注于自身的核心逻辑。MVC是对系统复杂性的一种合理的梳理与切分,它的思想实质就是“关注点分离”。至于MVC三元素的职责划分与相互关系,这里不再赘述,下图给出了非常细致的说明。

 

从MVC框架看MVC架构的设计

图1:MVC组件的功能和关系[i]

View与Controller的解耦:mediator+二次事件委派

笔者早年开发基于swing的GUI应用时,在架构MVC的实践过程中深刻体会到了view与controller之间的紧密耦合问题。在很多事件驱动的GUI框架里,如swing,用户对view的任何操作都会触发一个事件,然后在listener的响应方法里进行处理。如果让view自己注册成为事件的listener,则必须要在view中加入对controller的引用,这不是MVC希望看到的,因为这样view和controller就形成了紧密的耦合关系。若将controller注册为listener,则事件响应将由controller承担,这又会导致controller处理其不该涉及的展现逻辑,造成view和controller难以解耦的原因在于:多数的用户请求都包含一定成分的展现逻辑和一定成分的业务逻辑,两种逻辑揉合在一个请求里,在处理的时候,view与controller很难合理地分工。解决这一问题的关键是要在view与controller之间建立一种可以将展现逻辑与业务逻辑进行有效分割的机制,在这方面,PureMVC[ii]的设计非常值得参考,它通过引入mediator+二次事件委派机制很好的解决了view与controller之间的紧耦合问题。

Mediator是一种设计模式,这种模式在组件化的图形界面框架中似乎有着普遍的应用场景,即使是在四人帮的《设计模式》一书中,也使用了一个图形界面程序的示例来讲解mediator。mediator的设计用意在于通过一个媒介对象,完成一组对象的交互,避免对象间相互引用,产生复杂的依赖关系。mediator应用于图形界面程序时,往往作为一组关系紧密的图形组件的交互媒介,完成组件间的协调工作(比如点选某一按钮,其他组件将不可用)。在PureMVC中,mediator被广泛应用,其定位也发生了微妙的变化,它不再只是图形组件间的媒介,同时也成为了图形组件与command之间的媒介,这使得它不再是可选的,而是成为了架构中的必需设施。对应到传统MVC架构中,mediator就是view与controller之间的媒介(当然,也依然是view之间的媒介),所有从view发出的用户请求都经过了mediator再传递给controller,它的出现在一定程度上缓解了view与controller的紧密耦合问题。

当view、mediator和controller三者被定义出来,并进行了清晰的职责划分后,剩下的问题就是如何将它们串联起来,以完成一个用户请求了,在这方面,事件机制起到了至关重要的作用。事件机制可以让当前对象专注于处理其职责范围内的事务,而不必关心超出部分由谁来处理以及怎样处理,当前对象只需要广播一个事件,就会有对此事件感兴趣的其他对象出来接手下一步的工作,当前对象与接手对象之间不存在直接依赖,甚至感知不到彼此的存在,这是事件机制被普遍认为是一种松耦合机制的重要原因。讲到这里插一句题外话,在领域驱动设计(Domain-Driven Design)里,著名的Domain Event模式也是有赖于事件机制的这一特性被创造出来的,其用意正是为了保证领域模型的纯净,避免领域模型对repository和service的直接依赖。回到PureMVC, 我们来看在处理用户请求的过程中,事件机制是如何串联view、mediator和controller的。在PureMVC里,当一个用户请求下达时,图形组件先在自身的事件响应方法中实现与自身相关的展现逻辑,然后收集数据,将数据置入一个新的event中,将其广播出去,这是第一次事件委派。这个event会被一个mediator监听到,如果处理该请求需要其他图形组件的协助,mediator会协调它们处理应由它们承担的展现逻辑,然后mediator再次发送一个event(这次的event在PureMVC里称之为notification),这个event会促使某个command执行,完成业务逻辑的计算,这是第二次事件委派。在两次事件委派中,第一次事件委派让当事图形组件完成“处理其职责范围内的展现逻辑”后,得以轻松“脱身”,免于被“协调其他图件处理剩余展现逻辑”和“选择并委派业务对象处理业务逻辑”所拖累。而“协调其他图形组件处理剩余展现逻辑”显然是mediator的职责,于是第一次广播的事件被委派给了mediator. mediator在完成图形组件的协调工作后,并不会插手“选择并委派业务对象处理业务逻辑”的工作,这不是它的职责,因此,第二次事件委派发生了,一个新的event由mediator广播出去,后被某个command响应到,由command完成了最后的工作——“选择并委派业务对象处理业务逻辑”。

从MVC框架看MVC架构的设计

图2:mediator+二次事件委派机制

总结起来,PureMVC是通过在view与controller之间引入mediator,让view与controller变成间接依赖,用户请求从view到mediator,再从mediator到controller均以事件方式委派,mediator+二次事件委派的组合可以说是一种“强力”的解耦机制,它实现了view与controller之间的完全解耦。

从Controller到Command,自然粒度的回归

目前,很多平台的主流MVC框架在设计上都引入了command模式,command模式的引入改变了传统MVC框架的结构,受冲击最大的就是controller。在过去传统的MVC架构里,一个controller可能有多个方法,每个方法往往对应一个user action,因此,一个 controller往往对应多个user action,而在基于command的MVC架构里,一个command往往只对应一个user action。传统MVC架构里将一个user action委派到某个controller的某个方法的过程,在基于command的MVC架构里变成了将useraction与command一一绑定的过程。如果说传统controller的管理方式是在user action与model之间建立“集中式”的映射,那么基于command的管理方式就是在user action与model之间建立“点对点式”的直连映射。

从MVC框架看MVC架构的设计

图3:从基于Controller到基于Command的架构演进

主流MVC框架向command转型是有原因的,除了command自身的优势之外,一个非常重要的原因就是:由于缺少合理的组织依据,controller的粒度很难拿捏。controller不同于view与model,view与model都有各自天然的粒度组织依据,view的组织粒度直接承袭用户界面设计,model的组织粒度则是依据某种分析设计思想(如OOA/D)进行领域建模的结果,controller需要同时协调view与model,但是view与model的组织结构和粒度都是不对等的,这就使得controller面临一个“在多大视图范围内沟通与协调多少领域对象”的问题,由于找不出合理的组织依据,设计者在设计controller时往往感到无所适从。相比之下,command则完全没有controller的困惑,因为command有一个天然的组织依据,这就是user action。针对一个user action设计一个command,然后将两者映射在一起,是一件非常自然而简单的事情。不过,需要说明的是这并不意味着所有command的粒度是一样的,因为不同的user action所代表的业务量是不同的,因此也决定了command是有“大”有“小”的。遵循良好的设计原则,对某些较“大”的command进行分解,从中抽离出一些可复用的部分封装成一些较“小”的command是值得推荐的。很多MVC框架就定义了一些相关的接口和抽象类用于支持基于组合模式的命令拼装。

不管是基于controller还是基于command,MVC架构中界定的“协调view与model交互”的控制器职责是不会变的,都需要相应的组件和机制去承载与实现。在基于command的架构里,command承担了过去controller的部分职责,从某种意义上说 command是一种细粒度的controller,但是command的特性是偏“被动”的。一方面,它对于view和model的控制力比controller弱化了很多, 比如,一般情况下command是不会直接操纵view的。另一方面,它不知道自己与什么样的user action映射在了一起,也不知道自己会在何种情况下被触发执行。支撑command的运行需要额外的注册、绑定和触发机制,是这些机制加上command一起实现了controller的职责。由于现在多数基于command的MVC框架都实现并封装了这些重要的机制,所以从某种意义上说,是这些框架自身扮演了controller角色。

小结

本文主要分析了过去传统MVC架构中存在的两大弊病:view与controller的紧密耦合以及controller粒度难以把控的问题,介绍了一些MVC框架是如何应对这些问题的,这些设计方案所体现出的优秀设计思想是非常值得学习的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126204.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Vue生命周期函数执行顺序_生命周期函数

    Vue生命周期函数执行顺序_生命周期函数createdcreated在刚进入页面的时候,会触发该函数的方法。只在页面刚开始加载时执行一次。activated一进入页面就触发;可用于初始化页面数据等

    2022年8月30日
    1
  • 卸载 x 雷某度!GitHub 标星 1.5w+,从此我只用这款全能高速下载工具!

    卸载 x 雷某度!GitHub 标星 1.5w+,从此我只用这款全能高速下载工具!作者|Rocky0429来源|Python空间大家好,我是Rocky0429,一个喜欢在网上收集各种资源的蒟蒻…网上资源眼花缭乱,下载的方式也同样千奇百怪,比如BT下载,磁力链接,网盘资源等等等等,下个资源可真不容易,不一样的方式要用不同的下载软件,因此某比较有名的x雷和某度网盘成了我经常使用的工具。作为一个没有钱的穷鬼,某度网盘几十kb的下载速度让我…

    2022年6月14日
    31
  • ipset iptables_怎么把黑名单变白名单

    ipset iptables_怎么把黑名单变白名单使用ipset设置iptables(黑/白)名单一、ipset原理ipset是iptables的扩展,它允许你创建匹配整个地址集合的规则。而不像iptables只能匹配单个ip,避免维护的ip条目过多导致速度变慢,而ip集合存储在带索引的数据结构中,这种结构即时集合比较大也可以进行高效的查找,除了一些常用的情况,比如阻止一些危险主机访问本机,从而减少系统资源占用或网络拥塞,IPsets也具备一些新防火墙设计方法,并简化了配置。二、ipset安装1、ipset工具安装yum…

    2022年9月1日
    0
  • jquery ui 笔记

    jquery ui 笔记

    2022年1月3日
    145
  • emwin仪表控件_仪表开发工程师

    emwin仪表控件_仪表开发工程师本文导读i.MXRT1052具有增强型显示接口(eLCDIF),支持8/16/18/24位的数据总线模式。M1052核心板最高可支持1366×768的高清图像显示,芯片内置音频接口,搭载免费的emWin图形库可广泛应用多媒体设计。一、emWin介绍emWin是Segger公司针对嵌入式平台开发的稳定、高效的图形软件库,适合用于任何图形LCD的操作应用,并可输出高质量的无锯齿的文字和图形,通过调用…

    2022年10月14日
    0
  • 拉链表详解_拉链表还原统计

    拉链表详解_拉链表还原统计拉链表产生背景在数据仓库的数据模型设计过程中,经常会遇到这样的需求:1、数据量比较大;2、表中的部分字段会被update,如用户的地址,产品的描述信息,订单的状态等等;3、需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态,比如,查看某一个用户在过去某一段时间内,更新过几次等等;4、变化的比例和频率不是很大,比如,总共有1000万的会员,每天新增和发生变化的有10万左右;5、如果对这边表每天都保留一份全量,那么每次全量中会保存很多不变的信息,对存储是极大的

    2022年10月17日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号