Article / 文章中心

浅谈软件开发架构模式

发布时间:2022-01-17 点击数:583

作者 | 思禽

文章来源 | 阿里巴巴淘系技术团队


背景和问题



我个人平时会比较慎用“架构”这个词


1.一方面是觉得业界有很多架构大师和架构模式,而我的认知和实践有限;


2.另一方面是因为这个词看着挺高大上、有点务虚,如果不结合实际场景的具体问题来讨论,容易陷入“PHP是最好的语言”这样的辩论赛中。而不同场景中又有各自的问题,程序员们通过自己的理解和思考、针对实际场景对一些架构模式进行了扩展实践,以此来解决遇到的问题,也会基于同一个模式延伸出一些派生概念。


兵无常势,水无常形。 


所以,我个人的观点是:以要解决的问题为出发点,去讨论我们要采用的架构模式(技术方案)。


另外,由于我们是站在很多巨人肩膀上的,讨论时可以站在一些软件设计/开发原则的基础上。


写这篇文章,我也是从解决一些问题的目的出发的:


1.最近和团队同学讨论了相关话题,虽然大多数同学在实践上基本一致,但具体到话术、名词概念和具体使用的理解和实践上有些差异(这是很正常的,因为业界对同一个模式的理解和实践也不同)。我结合一些实际编码场景做了一番陈述,为了避免后续重复大费口舌,所以打算写下来,以后有需要直接发文章链接。


2.由于我个人的认知和实践有限,所以也希望能抛(huan)砖(ying)引(lai)玉(pen),让我学到更多。


3.虽然同一个架构模式在不同业务/技术领域的实施会有区别,但同一个团队内应该保持一致性,因为这样有助于日常的code review、功能模块的交接backup等活动,尤其是有利于使用统一的单测建设方案来保障我们的产品质量


    1. 实际问题:我最近在开发商家合并发货的功能,但由于之前基础发货功能的界面和逻辑并不是我开发的,所以我在修改原有代码、支持有非常多细节逻辑的合并发货能力时,就在担心对原有发(zhong)货(yao)能力的影响。而这时候,如果有单测的保障,我就可以更放心地进行功能升级改造了 —— 别说更复杂的合并发货能力了,而这类诉求在复杂的交易场景里很普遍。


提炼一下我遇到的具体问题:


在由不同开发人员持续迭代、进行功能升级的软件开发活动中,如何保障具有复杂逻辑的商家经营工具的产品质量。

软件开发活动

软件开发活动是整个流程的核心环节:接收产品和视觉设计需求/变更作为输入,然后输出客户可用的终端产品。


而统一的软件开发架构模式,则是我们保障软件开发质量的基础。(这里就不具体展开WHY了)


由于讨论的是具体面向客户使用的业务场景,少不了客户操作交互的视图层(View),所以我从MVC开始谈起。


从表现层的 MVC 谈起

表现层的 MVC


虽然我平时比较慎用“架构”这个词,但我平时喜欢随手拍一些建筑物。


因为建筑之美,会让我联想到软件的架构也应该有美感,毕竟Software Architecture这个概念也是起源于Architecture。


当然,由于软件的可移植性、可复用性,从某些角度来讲,软件架构相比建筑架构有其更复杂的地方。


这时候,架构这个词就会给我一种接地气的感觉:有多少块砖,每块砖做什么用、放到哪里去,这块砖 和 那块砖怎么黏在一起或互相支撑。


MVC诞生至今已经超过40年了(Since 1979),10多年前就得到过很广泛的讨论和实践,穿越时空到今天肯定有其反脆弱性和内在核心价值。


虽然如今乍看起来好像已经过气、被讨论过千百遍了,但仍然有很多程序员会有不同理解和看法,或多或少。


这是很正常的,上面也提到了部分原因,这里具体再展开下。


MVC 在经典三层架构里的位置


MVC是一种通用架构模式,早期PC时代应用在桌面客户端,后来在Web时代变得流行(我以前写PHP也用过相关MVC框架),如今在移动互联网时代也得到广泛应用。


上面这三个场景的应用,都是面向客户的,需要交互表现的。


从MVC命名中的View(视图)也可以看出,MVC模式应用在软件系统架构里的表现层。


在微软官方文档里,也明确把MVC放在Web Presentation Patterns下。

Web Presentation Patterns


我之所以没有在上图中对M-V-C添加箭头线条,是因为在这一点上,不同程序员也有不同理解和实践。(2)


这是第一个需要明确的点:MVC架构模式在多层系统架构里的应用范围。


左侧 业务表现层-业务服务层-基础服务层 是移动端三层架构模式,未涉及到 C/S 交互;右侧是Web B/S场景的三层架构模式。


因为有些应用会比较简单,根本不需要业务服务或基础服务层,纯粹靠一个MVC(或者VC)就能交付出一个Mobile/Web App;而且在一些业务系统里,Web前端/桌面客户端/移动App 也可能会被简化为 大前端/大终端表现层;所以可能基于不同信息,不同程序员对此会有不同认知。


但随着用户终端应用的重要性和复杂度的提升,已经从简单应用发展到复杂多团队协同的平台型或航母级应用,仅靠一个MVC来完成交付是不合适的。


也可以反过来想,程序员会把以下代码放在客户端代码的哪一层:


1.对Web引擎的扩展逻辑。


2.通信协议的结构定义,以及相应的socket连接和通信代码。


3.一个业务相关且UI无关的平台开放能力。


4.Crash捕获、卡顿监控、日志埋点等功能实现,比如Android在做APM相关事情时会采用AOP方式,利用ASM、AspectJ等方案来做字节码插桩。


5.……


业界基于 MVC 模式的不同实践


前面提到不同程序员对MVC模式的理解和实践存在差异,业界大厂亦然,比如苹果、微软,在自身实践MVC模式或者向开发者提供相关文档时,也一样有不同的说法。


  • 微软


微软2014年的文档描述


上图是微软2014年的文档描述。


微软2021年10月份更新的文档中使用了下图:


微软在MVC的实践


可以看出微软在MVC的实践上:


1.Controller可以引用View和Model。


2.View可以引用Model。


3.这里的Model倾向于是Passive。


值得注意的是,微软在文档里写到:


1.强类型视图通常使用 ViewModel 类型,旨在包含要在该视图上显示的数据。 控制器从模型创建并填充 ViewModel 实例。


2.控制器不应由于责任过多而变得过于复杂。 要阻止控制器逻辑变得过于复杂,请将业务逻辑推出控制器并推入域模型。


  • 苹果 & 斯坦福


苹果和斯坦福10年+前给的MVC模式图

上图分别是苹果和斯坦福10年+前给的MVC模式图。之所以把它们放在一起,是因为斯坦福这张图也是他们iOS开发课程上的课件,基本也是基于Apple的官方文档来的。


不过这份官方文档已经是Retired Document,并声明不一定是目前的最佳实践:


Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.


是的,上图应该产出于移动互联网方兴未艾的时候,随着移动互联网蓬勃发展,“最佳实践”被一路多种挑战 —— MVC也被戏称为Massive View Controller,如何重构Massive View Controller为Lighter View Controller也成为了一个专题。


上面两张图呈现出:


1.Controller引用View和Model,做update操作。


2.Model通过KVO和(广播)通知来触达Controller,驱动Controller做出响应。


3.View通过DataSource、Delegate、Target-Action等方式解耦、弱依赖Controller,由Controller对各种用户操作、UI渲染诉求做出响应。


4.View和Model之间是隔离的。


微软和苹果的异同点


1.相同点:Model包含 所需的数据结构封装,以及相应数据操作的方法定义。即Data + 本地或远端的CURD。


2.差异点:微软给的图中,View可以引用Model,而苹果给的图中不行。


一些问题和思考


1.View有箭头指向Model,这里的引用关系是指什么?是View持有Model.Data数据对象,还是View调用Model.CURD方法。


2.Controller的本意是Controing Logic,那除了ViewController外,是否还可以有其它的XxController,比如DataSourceController、NotificationController?


3.从命名上看,既然ViewController 既有View 又有Controller,那为什么把它放在 C里面,而不放在V里面呢?比如当我们在iOS/Android开发中引入MVVM模式后,ViewController或Activity属于M-VM-V的哪部分呢,代码放在哪个目录下呢?


4.我有类名使用ViewModel后缀就代表我使用MVVM模式了吗?目前看,上述微软文档里不这么认为。


  • Martin Fowler


作为:


1.《重构 : 改善既有代码的设计》、《企业应用架构模式》等著作的作者,


2.敏捷软件开发宣言创作者之一,


3.MVVM模式诞生地 —— 微软 —— 参考引用的技术专家,


Martin Fowler画的MVC模式图如下:


Martin Fowler画的MVC模式图

和上面微软、苹果给的图,又不一样了 *_*


  • Google


谷歌相关的描述则是:

谷歌相关的描述则



  • MVC 和 DDD


Martin Fowler和《领域驱动设计》作者Eric Evans也讨论过MVC中Model的设计理念:


1.贫血模型:将Model分为简(pin)单(xue)的Model数据对象,和处理操作数据对象的Service/Manager/BizLogic等。


示例:为aPerson修改name,则由 CitizenService.changeNameOfPerson( aPerson ) 这种方式来实现。


2.充血模型:将对应领域的处理逻辑放到领域模型中,使得这个领域模型更饱(chong)满(xue)。


示例:aPerson要刷牙,则由 aPerson.brushTeeth() 来实现。


补充:充血模型更有面向对象编程的味道,尤其是搭配交易领域等业务场景,更有体感。不过稍微细想一下,可能就会发现DDD对设计的要求会更高,从而对研发周期和质量保障提出了新的要求,并且可能引起对现有系统的大规模重构。(盒马的DDD实践)


也就是说,大到MVC各个模块的依赖引用关系,细到Model中的代码设计方式,业界都有不同的理念和实践。


Java Web开发领域也对Model的设计产生过非常激烈的讨论。


  • 小结


先抛开具体模块的代码设计方案,基于以上几种业界大厂或大佬的描述,我小结了以下这张图并标注了待解问题

MVC中Controller


问题一:如何解决MVC中Controller的膨胀臃肿问题?


要回答如何解决,需要先思考为什么膨胀。


问题二:View能否引用Model?


1.要回答能否引用,需要先定义引用关系是什么。


是持有对象,还是调用CURD接口操作对象。


又或者这两者没有必要区分,因为持有的对象本身就可能带CURD接口。


2.参考上面相关资料,目前(1)微软2021年文档支持可以引用;(2)苹果/斯坦福2011年文档反对


3.在DinamicX研发模式中,模板View持有data数据对象在内部进行填充和渲染。


问题三:存在View -> Model,那么是否可以反过来存在 Model -> View?


和问题二在描述上相反但又有关联,如果对问题再进一步提问的话:


1.使用 -> 引用关系,是为了解决什么问题?


2.使用 -> 引用关系,会产生什么问题?


如同文章开头所说,以上问题需要结合具体场景来展开(见 实际案例结合),尽量从务虚到务实。


Redux-like Architecture and Framework


随着前后端分离得更彻底,终端设备性能和用户体验重要性的提升,前端领域也得到了蓬勃发展,开发方式也有了比较大的变化,MVC-like方式不再是主流:


1.UI开发方面从早期的命令式到现在的声明式。


2.整体应用和业务逻辑实现方面,从早期的OOP写法,转向基于FP的响应式编程。比如Redux的数据流、React的Hook特性等。


3.各种框架蓬勃发展,一些概念和模式的提出、实践应用方面,我个人认为是领先并影响客户端的。


其中Redux是一个经典案例,并且我觉得Redux官方也挺开放包容的,比如Dan Abramov写的《You Might Not Need Redux》。


Redux

图源自Redux官网,在官网上是一张很形象生动的动图。


和MVC延伸派生出的MVC Family一样,Redux提出或重新带火了数据流、状态管理等概念,开始影响其它平台领域,并诞生了一些框架。


比如ReSwift、swift-composable-architecture,以及SwiftUI里的State and Data Flow。


MVC Family

虽然我也写过点React,但并没有怎么实践过Redux。


不过这些变化和影响,是我们在解决问题的过程中需要结合考虑的。


实际案例结合


Redux-like Architecture and Framework


程序 = 数据结构 + 算法。—— Nicklaus Wirth,Pascal之父,图灵奖获得者


这句话乍看起来可能会有点面向过程设计的感觉,但OOP中的对象其实也是由数据+方法组成,而FP则更不用说了。


MyContact的数据结构


在编码开发活动中,会存在以上3种数据结构定义和使用方式:


1.原生数据结构,比如list/array、map/dictionary、tuple等。


2.类似MyContact的数据结构定义,由服务端返回的数据进行转化,并可能根据业务逻辑按需加上一些标志位给Controller消费。


3.类似ContactViewModel这样的纯粹为视图View服务的数据结构定义。


补充:


(1)MyContact 和 ContactViewModel 只是特意区分的命名,实际上 MyContact 也可以是纯粹为视图View服务的数据结构定义。


(2)但是,合适的命名有助于帮助我们思考和编码,从表达上呈现出我们的倾向和重点。


"There are only two hard things in Computer Science: cache invalidation and naming things."

—— Phil Karlton


常见的多复杂卡片的列表场景


这个场景可以部分回答问题一:为什么Controller会膨胀,以及如何解决。


其它部分答案则落在复杂页面场景的多delegate、target-action、notification-observer等视图交互响应的处理逻辑上。


我认为,苹果/斯坦福之前反对View引用Model,就是导致MVC变成Massive View-Controller的一个原因。


另一个原因我认为是工具链只提供了ViewController这样的Controller模板,隐式教导开发者都在这里写代码。


这也可能是因为十几年前移动互联网还没发展起来,移动App的复杂度低,所以苹果提供了在当时简单够用的方案。

Controller

当只能由Controller 持有-> Model的时候,那么在多复杂卡片的列表场景中,必须由Controller来更新每个View的属性/状态。


1.MyViewController需要为ContactCell更新它的各种相关属性,类似的还有AddressCell、PackageCell等。


2.MyViewController在更新AddressCell展示前,可能还需要先为它计算出合适的富文本展示内容。


3.MyViewController需要响应不同Cell的点击交互行为,包含但不限于按钮点击、输入框变化、富文本跳转、键盘起落等。


4.MyViewController需要响应CollectionView/TableView的DataSource/Delegate各种方法实现。


5.MyViewController需要响应Model层的变更通知,或者是另外一个ViewController抛过来的广播通知。


6.……


然后MyViewController就爆炸了。


针对这种场景,我的解法是:


1.通过让View->Model,基于工厂模式,把组件化Cell基于数据更新的布局逻辑交给View负责,如contactCell.configUIWithModel( contactModel )。这样有点类似上面DDD提到的充血Model,具备高内聚的特点,带来好处:


    a.和微软说的减轻控制器负担、推入域模型类似,通过把数据驱动布局的代码推入组件域内,减轻了MyViewController的负担。


    b.利于做这部分组件化Cell的UI测试。


    c.利于这些组件化视图复用到其它场景,比如交易管理场景的订单卡片可以复用到搜索场景中,不用在SeachViewController里复制粘贴一大堆代码,只需要从Model取一个数据对象丢给组件化Cell即可。


    工厂模式下,产品的刷漆、烘干、印花等操作会在内部完成,不会丢一个模型让客户去自己贴logo。MVC的每一部分都可以用不同的设计模式来组合实施,实现解耦或动态灵活的目标。


2.基于ViewController,拆分出不同职责的扩展,比如MyViewController+Delegate专门复杂响应代理事件处理。


3.定义出其它类型的Controller,比如MyDataSourceController,专门为TableView提供数据源,可以类比参考Android中ListView的Adapter+ViewHolder。


对应下图:


Adapter+ViewHolder

到这里已经回答了前面的问题一和问题二。


更多解法可以参考上面提到的微软给的建议,以及lighter view controllers。


这里采用了 VIew -> Model 的方案,用来参与解决Massive View-Controller的问题,并且让View更容易复用和做UI测试,带来了好处。


可以结合微软提的


“控制器不应由于责任过多而变得过于复杂。 要阻止控制器逻辑变得过于复杂,请将业务逻辑推出控制器并推入域模型”


再进一步讨论下。


我的理解和举例:


1.存在一个输入框,让用户提交物流单号。


2.用户在输入过程或者完成输入后,由View通过delegate模式路由给Controller做校验,而Controller可能还要进一步依赖Model去做更完整的校验(如网络请求到服务端,因为物流单号的规则很多而且可能动态更新)。


3.当Controller责任过多、代码膨胀、过于复杂时,就将物流单号这块业务逻辑推入 物流(单号)域模型中,即由View直接通过delegate模式交给 LogisticsModel来做校验。


4.也是 View -> Model 。


不一定对,抛(huan)砖(ying)引(lai)玉(pen)。


那么,存在VIew -> Model,有什么坏处吗?


一个 Kotlin 跨平台场景案例


这里不具体展开讲Kotlin及其跨平台相关内容,只是描述从MVC模式做跨平台迁移时遇到的问题。

 Kotlin 跨平台场景案例


这里的ViewController/Activity放在哪里,也和上面的一个问题相呼应。


虽然D-KMP主张通过全新构建工程写代码的方式来实践,但从实际情况出发,绝大多数现有系统都会是以单点尝试、渐进式的方式来落地,或发展、或回撤。


那么,如上图所示:


1.如果在既有MVC代码结构中,View -> Model,在这种渐进式迁移场景下,就需要修改View来适配新的ViewModel。


2.如果 View 不引用 Model,则是由 Controller来做胶水层设置更新视图(命令式UI)。


3.后者的好处是,保持View的独立性,只需要修改这个业务场景对应的单个Controller即可。因为View具有可复用性,可能在另外还没准备迁移/改变的模块里也有使用。


所以,此处不建议 View -> Model。


那么,谁对呢?


谁对谁错的务虚讨论


当有程序员要推荐使用其它架构模式的时候,通常开头的一句话就是先说MVC模式的问题。


比如ReSwift在写 Why ReSwift? 时,开头第一句话就是:Model-View-Controller (MVC) is not a holistic application architecture.


我个人认为:


1.架构是一个名词(n.)+动词(v.)。


架构(n. & v.)是为了帮助 开发者在交流时有一致的理解、在业务需要时能够便于扩展、在出问题时能够快速定位等等(对抗熵增)。具体还是看采用这个架构的得失,要解决什么问题或带来什么好处,然后带来什么成本或付出什么代价。


架构(v.)通过分配每部分代码的职责并为他们取名(好比iOS/Android开发工程师这样的岗位名称,让别人一看就知道是做什么的),然后几个名字加在一起 形成了架构模式这个抽象概念。


从具体到抽象,然后再由这个抽象概念去指导程序员实践写代码,促进了架构的传播,比如MVC、MVP、MVVM、MVVM-C、MVI、MV-Whatever,VIPER,Redux and More……


2.有时候并不一定是架构模式的错,还有可能是平台/框架在让架构模式自传播时采用的具体方案出了点问题,又或者是开发者自己写代码的问题。


3.有时候架构模式之间也不是互斥的,也可以在不同场景下互补。比如在MVC里,每个业务表现模块不一定要有三个元素齐聚,甚至也可以 VC-M-VC 共用一个M(可参考斯坦福iOS开发课程内容)。


有一个实际的业务场景是这么实践的:

MVC和MVVM



MVC和MVVM在一个业务场景里相结合。


这里提到了MVVM,前面的Kotlin跨平台图,除了涉及到 View -> Model 的引用关系是否应该存在的讨论,也涉及到了Declarative-UI和MVVM架构模式等概念,再顺便展开下。


  • MVVM 和 MVW


MVC、MVP 和 Declarative-UI 这三个名词概念都有几十年历史了,其中声明式UI在近十年又开始火了起来;而MVVM则有十几年历史。


微软在2005年提出了MVVM概念,那么微软会不会认为 MVVM 存在谁对谁错的问题呢?


在我阅读完几篇很赞的微软官网的文章后,我的感觉是微软很谦虚和海纳百川,并不会很教条式。


比如:


1.在微软MVC官方文档中,建议强类型视图使用 ViewModel 类型,旨在包含要在该视图上显示的数据。 控制器从模型创建并填充 ViewModel 实例。也就是MVC里也可以用ViewModel。


2.微软认为自己在2005年提出的MVVM架构模式,有些内容和Martin Fowler在2004年提出的PM(表现层模型)概念一致。


3.微软认为在架构模式上存在着不完全实现或组合实现的情况,也不会跑去说谷歌Android的ViewModel不符合他提出来的规范。


给我的感觉就好像Angular一样:


I hereby declare AngularJS to be MVW framework - Model-View Whatever. Where Whatever stands for "whatever works for you".


当然,微软也给出了他关于MVVM的一些描述:


1.V和VM是一对多的关系,即View 1:n ViewModel。相比于 MVC里 Controller 1:n View,显然MVC的C更容易膨胀,所以上面也提到了使用多Controller方案。


2.V引用VM,VM操作M,而VM不需要引用V(和MVP不同)。VM作为V的上下文,包含V所需用来展示的数据,响应V被用户触发的事件。


3.V对M无感知,反过来 M 对 V和VM 也无感知。整体是松散解耦的模式。


4.当VM的属性值发生变化时,通过数据绑定方式传达给V。这就需要有机制支撑,有对应的机制MVC也能做数据绑定。


基于上述描述我画了下图:

MVVM

总体来说,MVVM是基于事件驱动的、以数据绑定机制为支撑的松散解耦架构模式。


1.它的前提是有系统/平台/框架的机制支撑,不然实现成本和复杂度有点大。


2.相比MVC,它的优势是视图控制逻辑不太会膨胀,代码单元更容易被测试,在可读性、可维护性上更好。


3.它可能带来的成本/问题是入门上手较难,简单场景下使用容易过度设计,复杂场景下出问题调试比较麻烦。


考虑到ViewController在该场景的薄胶水特质,以及也参与视图展示和用户响应,所以我把它放在V里。


谁对谁错的务实案例:VIPER 和分层演变


关于谁对谁错/谁好谁坏,可以再来看一个案例。


  • VIPER架构模式的应用


VIPER架构模式


图来源于Bohdan Orlov的《iOS Architecture Patterns》


从图中可以看到ViewController在VIPER中也被放到View里了,而且连接词用的是 and / or 。


我之前写过MVC、MVP、MVVM、VIPER等架构模式下的代码,完全实现或不完全实现。


有一次,我在某个业务表现模块里应用VIPER,然后定义了一个XxxNetworkInteractor类,用来负责做网络请求。


VIPER架构模式

  • VIPER架构模式的演变


后来随着业务场景的变化,一个业务模块可能需要对接多套网关服务,XxxNetworkInteractor就要做抽象隔离,消除业务逻辑对多套网关请求的感知,并且应用到其它业务模块。


这就有点像 VPER - I - VPER 模式 —— 多个业务模块对 Interactor 进行了复用。

XxxNetworkInteractor

  • 进化


1.随着对接网关数的增加、网关能力的增强,XxxNetworkInteractor也配套建设了更多能力。


2.随着使用XxxNetworkInteractor的模块越来越多,不同业务的一些通用诉求和处理逻辑也随之增加。


3.随着XxxNetworkInteractor的功能越来越强大,代码越来越多,逐渐需要抽离出一个单独模块,不管是从提高编译速度,还是从封装暴露等角度。


XxxNetworkSDK.framework诞生了。

XxxNetworkSDK.framework

  • 退化


再后来,随着技术和业务的变化,底层网关服务ATop成为了领域范围内的事实标准,之前遇到的问题也消失了,而XxxNetworkSDK又带来一些成本,比如要配套ATop的能力升级进行迭代维护。


此时,XxxNetworkSDK可以退出历史舞台了,让业务模块直接面向ATop编程,这样既降低了维护成本,又带来了一定的包大小收益。


配套ATop的能力

以后,假如有另外一个领域范围的业务场景(采用了XTop),要复用最上层的业务模块,由于在另外一个领域范围,业务模块要从 调用ATop 改为调用XTop。


那么,接手或负责迁移复用的开发者,会不会在想:为什么这些业务模块直接调用ATop,而没有使用一个XxxNetworkInteractor来做抽象隔离呢?


1.如果有,他只要做一件事情:让XxxNetworkInteractor对接XTop即可。


2.如果没有,他需要去替换修改每个业务模块的调用代码、返回值处理等等。


写在最后,回到问题


不管是务虚的讨论,还是务实的案例,或者是微软和Angular的“whatever works for you”的观点,结论是没有谁对谁错、谁好谁坏,只有实际场景下要解决的核心问题。


兵无常势,水无常形。


那么,回到想要解决的问题:


在由不同开发人员持续迭代、进行功能升级的软件开发活动中,如何保障具有复杂逻辑的商家经营工具的产品质量。


我的想法是以可测性作为手段,来保障功能升级改造或代码重构后,可在合理时间范围内得到充分回归验收,保障相关组件、模块或整体产品的质量。


具体的方案实施上,因为是以可测性为重要关注点,再结合目前的技术方向,我会倾向于采用MVVM。


1.MVVM的架构模式在理解和认知上比较成熟(应该是移动端开发领域TOP2流行的),便于实施和传播,并通过模式的名称来体现要强调的关注点:可测性。


2.MVVM可以更好地结合SwiftUI+Combine、Kotlin跨平台等技术方向。


3.MVVM完全版的上手门槛和简单场景的过度设计问题,可以通过采用不完全版MVVM来解决。


不完全版的MVVM可以cover适用于简单场景的MVC,近似于 超集-子集 关系。


虽然也可以通过多Controller的方式来解决MVC膨胀问题,但MVC的命名在实践中就容易让程序员弱化掉可测性这个关注点(也可能是我个人理解和实践不够正确)。


4.复杂场景的调试问题、更多可测性的实践,需要再摸索下,也希望得到相关分享和指点。