首页 » SEO优化 » php耦合技巧_软件开拓架构模式浅谈一些思虑和实践记录

php耦合技巧_软件开拓架构模式浅谈一些思虑和实践记录

访客 2024-10-27 0

扫一扫用手机浏览

文章目录 [+]

其余,由于我们是站在很多巨人肩膀上的,谈论时可以站在一些如SOLID等软件设计/开拓原则的根本上。

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

php耦合技巧_软件开拓架构模式浅谈一些思虑和实践记录

最近和团队同学谈论了干系话题,虽然大多数同学在实践上基本同等,但详细到话术、名词观点和详细利用的理解和实践上有些差异(这是很正常的,由于业界对同一个模式的理解和实践也不同)。
我结合一些实际编码场景做了一番陈述,为了避免后续重复大费口舌,以是打算写下来,往后有须要直接发文章链接。
由于我个人的认知和实践有限,以是也希望能抛(huan)砖(ying)引(lai)玉(pen),让我学到更多。
虽然同一个架构模式在不同业务/技能领域的履行会有差异,但同一个团队内该当保持同等性,由于这样有助于日常的code review、功能模块的交卸backup等活动,尤其是有利于利用统一的单测培植方案来保障我们的产品质量。
实际问题:我最近在开拓商家合并发货的功能,但由于之前根本发货功能的界面和逻辑并不是我开拓的,以是我在修正原有代码、支持有非常多细节逻辑的合并发货能力时,就在担心对原有发(zhong)货(yao)能力的影响。
而这时候,如果有单测的保障,我就可以更放心地进行功能升级改造了 —— 别说更繁芜的合并发货能力了,而这类诉求在繁芜的交易场景里很普遍。

提炼一下我碰着的详细问题:

php耦合技巧_软件开拓架构模式浅谈一些思虑和实践记录
(图片来自网络侵删)

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

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

而统一的软件开拓架构模式,则是我们保障软件开拓质量的根本。
(这里就不详细展开WHY了)

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

二 从表现层的MVC谈起

虽然我平时比较慎用“架构”这个词,但我平时喜好随手拍一些建筑物。
由于建筑之美,会让我遐想到软件的架构也该当有美感,毕竟Software Architecture这个观点也是起源于Architecture。
这时候,架构这个词就会给我一种接地气的觉得:有多少块砖,每块砖做什么用、放到哪里去,这块砖 和 那块砖怎么黏在一起或相互支撑。
当然,由于软件的可移植性、可复用性,从某些角度来讲,软件架构比较建筑架构有其更繁芜的地方。

MVC出身至今已经超过40年了(Since 1979),10多年前就得到过很广泛的谈论和实践,穿越时空到本日肯定有其反薄弱性和内在核心代价。
虽然如今乍看起来彷佛已经由气、被谈论过千百遍了,但仍旧有很多程序员会有不同理解和意见,或多或少。
这是很正常的,上面也提到了部分缘故原由,这里详细再展开下。

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

MVC是一种通用架构模式

早期PC时期运用在桌面客户端,后来在Web时期变得盛行(我以前写PHP也用过干系MVC框架),如今在移动互联网时期也得到广泛运用。

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

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

在业界某有名公司的官方文档里,也明确把MVC放在Web Presentation Patterns下。

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

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

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

由于有些运用会比较大略,根本不须要业务做事或根本做事层,纯粹靠一个MVC(或者VC)就能交付出一个Mobile/Web App;

而且在一些业务系统里,Web前端/桌面客户端/移动App 也可能会被简化为 大前端/大终端表现层;

以是可能基于不同信息,不同程序员对此会有不同认知。

但随着用户终端运用的主要性和繁芜度的提升,已经从大略运用发展到繁芜多团队协同的平台型或航母级运用,仅靠一个MVC来完成交付是不得当的。

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

对Web引擎的扩展逻辑。
通信协议的构造定义,以及相应的socket连接和通信代码。
一个业务干系且UI无关的平台开放能力。
Crash捕获、卡顿监控、日志埋点等功能实现,比如Android在做APM干系事情时会采取AOP办法,利用ASM、AspectJ等方案来做字节码插桩。
……

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

前面提到不同程序员对MVC模式的理解和实践存在差异

业界大厂亦然,以下会结合业界一些有名且有影响力的公司在MVC模式上的实践,做进一步的展开谈论。

有名公司A

有名公司A在辅导开拓者利用MVC时,推举下图办法:

可以看出在他们的实践上:

Controller可以引用View和Model。
View可以引用Model。
这里的Model方向于是Passive。

同时,他们建议:

在强类型视图场景,掌握器从模型创建并添补ViewModel实例,该ViewModel 实例包含要在该视图上显示的数据。
当掌握器由于任务过多而变得过于繁芜时,也便是业界戏称的“MVC means Massive View Controller”,须要将业务逻辑从掌握器移出并推入域模型中。

有名公司B

说到Massive View Controller,有名公司B在移动互联网朝阳东升的时候,推举下图所示的MVC实践方案:

上图呈现出:

Controller引用View和Model。
Model通过一些松耦合办法来触达Controller,如广播关照、callback等,驱动Controller做出相应。
View通过代理模式等方案弱依赖Controller,由Controller对各种用户操作、UI渲染诉求做出相应。
而View和Model之间是隔离的,Model变革后对View的更新操作全部由Controller卖力。

不过相应的官方文档已经被声明是过期文档了,并备注不一定是目前的最佳实践。

是的,随着移动互联网发达发展,十年前的“最佳实践”被一起多种寻衅 —— 在采取这种方案的开拓领域中,如何重构Massive View Controller为Lighter View Controller已经成为了一个专题。

比拟和思考

A和B的异同点

相同点:Model包含 所需的数据构造封装,以及相应数据操作的方法定义。
即Data + 本地或远真个CURD。
差异点:在有名公司A给的图中,View可以引用Model,而在有名公司B给的图中则弗成。

一些问题和思考

View有箭头指向Model,这里的引用关系是指什么?是View持有Model.Data数据工具,还是View调用Model.CURD方法。
Controller的本意是Controing Logic,那除了ViewController外,是否还可以有其它的XxController,比如DataSourceController、NotificationController?从命名上看,既然ViewController 既有View 又有Controller,那为什么把它放在 C里面,而不放在V里面呢?比如当我们在iOS/Android开拓中引入MVVM模式后,ViewController或Activity属于M-VM-V的哪部分呢,代码放在哪个目录下呢?我有类名利用ViewModel后缀就代表我利用MVVM模式了吗?

Martin Fowler

作为

《重构 : 改进既有代码的设计》、《企业运用架构模式》等著作的作者;敏捷软件开拓宣言创作者之一;MVVM模式出身时参考引用的技能专家。

Martin Fowler给的MVC模式图如下:

和上面有名公司A和B的图,又不一样了,不过他这里也因此为View可以引用Model的。

MVC和DDD

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

血虚模型:将Model分为简(pin)单(xue)的Model数据工具,和处理操作数据工具的Service/Manager/BizLogic等。
示例:为aPerson修正name,则由 CitizenService.changeNameOfPerson( aPerson ) 这种办法来实现。
充血模型:将对应领域的处理逻辑放到领域模型中,使得这个领域模型更饱(chong)满(xue)。
示例:aPerson要刷牙,则由 aPerson.brushTeeth() 来实现。
补充:充血模型更有面向工具编程的味道,尤其是搭配交易领域等业务场景,更有体感。
不过轻微细想一下,可能就会创造DDD对设计的哀求会更高,从而对研发周期和质量保障提出了新的哀求,并且可能引起对现有系统的大规模重构。
(盒马的DDD实践)

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

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

小结

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

问题一:如何办理MVC中Controller的膨胀臃肿问题?

要回答如何办理,须要先思考为什么膨胀。

问题二:View能否引用Model?

要回答能否引用,须要先定义引用关系是什么。
是持有工具,还是调用CURD接口操为难刁难象。
又或者这两者没有必要区分,由于持有的工具本身就可能带CURD接口。
参考上面干系资料,目前业界有的支持、有的反对。

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

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

利用 -> 引用关系,是为理解决什么问题?利用 -> 引用关系,会产生什么问题?

犹如文章开头所说,以上问题须要结合详细场景来展开(见 实际案例结合),只管即便从务虚到务实。

3 Redux-like Architecture and Framework

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

UI开拓方面从早期的命令式到现在的声明式。
整体运用和业务逻辑实现方面,从早期的OOP写法,转向基于FP的相应式编程。
比如Redux的数据流、React的Hook特性等。
各种框架发达发展,一些观点和模式的提出、实践运用方面,我个人认为是领先并影响客户真个。
个中Redux是一个经典案例,并且我以为Redux官方也挺开放原谅的,比如Dan Abramov写的《You Might Not Need Redux》。

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

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

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

不过这些变革和影响,是我们在办理问题的过程中须要结合考虑的。

三 实际案例结合

1 常见的数据构造定义和利用

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

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

在编码开拓活动中,会存在以上3种数据构造定义和利用办法:

原生数据构造,比如list/array、map/dictionary、tuple等。
类似MyContact的数据构造定义,由做事端返回的数据进行转化,并可能根据业务逻辑按需加上一些标志位给Controller消费。
类似ContactViewModel这样的纯粹为视图View做事的数据构造定义。

补充:(1)MyContact 和 ContactViewModel 只是特意区分的命名,实际上 MyContact 也可以是纯粹为视图View做事的数据构造定义。
(2)但是,得当的命名有助于帮助我们思考和编码,从表达上呈现出我们的方向和重点。

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

2 常见的多繁芜卡片的列表场景

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

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

我认为,之前反对View引用Model,便是导致MVC变成Massive View-Controller的一个缘故原由。

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

这也可能是由于十几年前移动互联网还没发展起来,移动App的繁芜度低,以是供应了在当时大略够用的方案。

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

MyViewController须要为ContactCell更新它的各种干系属性,类似的还有AddressCell、PackageCell等。
MyViewController在更新AddressCell展示前,可能还须要先为它打算出得当的富文本展示内容。
MyViewController须要相应不同Cell的点击交互行为,包含但不限于按钮点击、输入框变革、富文本跳转、键盘起落等。
MyViewController须要相应CollectionView/TableView的DataSource/Delegate各种方法实现。
MyViewController须要相应Model层的变更关照,或者是其余一个ViewController抛过来的广播关照。
……

然后MyViewController就爆炸了。

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

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

a.和减轻掌握器包袱、推入域模型类似,通过把数据驱动布局的代码推入组件域内,减轻了MyViewController的包袱。

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

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

2.基于ViewController,拆分出不同职责的扩展,比如MyViewController+Delegate专门繁芜相应代理事宜处理。

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

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

对应下图:

到这里已经回答了前面的问题一和问题二。
更多解法可以参考上面提到的干系建议,比如lighter view controllers

这里采取了 VIew -> Model 的方案,用来参与办理Massive View-Controller的问题,并且让View更随意马虎复用和做UI测试,带来了好处。

可以结合前面提到的

“当掌握器由于任务过多而变得过于繁芜时,须要将业务逻辑从掌握器移出并推入域模型中。

再进一步谈论下。

我的理解和举例:

存在一个输入框,让用户提交物流单号。
用户在输入过程或者完成输入后,由View通过delegate模式路由给Controller做校验,而Controller可能还要进一步依赖Model去做更完全的校验(如网络要求到做事端,由于物流单号的规则很多而且可能动态更新)。
当Controller任务过多、代码膨胀、过于繁芜时,就将物流单号这块业务逻辑推入 物流(单号)域模型中,即由View直接通过delegate模式交给 LogisticsModel来做校验。
也是 View -> Model 。

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

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

3 一个Kotlin跨平台场景案例

这里不详细展开讲Kotlin及其跨平台干系内容,只是描述从MVC模式做跨平台迁移时碰着的问题。

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

虽然D-KMP主见通过全新构建工程写代码的办法来实践,但从实际情形出发,绝大多数现有系统都会因此单点考试测验、渐进式的办法来落地,或发展、或回撤。

那么,如上图所示:

如果在既有MVC代码构造中,View -> Model,在这种渐进式迁移场景下,就须要修正View来适配新的ViewModel。
如果 View 不引用 Model,则是由 Controller来做胶水层设置更新视图(命令式UI)。
后者的好处是,保持View的独立性,只须要修正这个业务场景对应的单个Controller即可。
由于View具有可复用性,可能在其余还没准备迁移/改变的模块里也有利用。

以是,此处不建议 View -> Model。
那么,谁对呢?

4 谁对谁错的务虚谈论

当有程序员要推举利用其它架构模式的时候,常日开头的一句话便是先说MVC模式的问题。

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

我个人认为

架构是一个名词(n.)+动词(v.)。
架构(n. & v.)是为了帮助 开拓者在互换时有同等的理解、在业务须要时能够便于扩展、在出问题时能够快速定位等等(对抗熵增)。
详细还是看采取这个架构的得失落,要办理什么问题或带来什么好处,然后带来什么本钱或付出什么代价。
架构(v.)通过分配每部分代码的职责并为他们取名(好比iOS/Android开拓工程师这样的岗位名称,让别人一看就知道是做什么的),然后几个名字加在一起 形成了架构模式这个抽象观点。
从详细到抽象,然后再由这个抽象观点去辅导程序员实践写代码,促进了架构的传播,比如MVC、MVP、MVVM、MVVM-C、MVI、MV-Whatever,VIPER,Redux and More……有时候并不一定是架构模式的错,还有可能是平台/框架在让架构模式自传播时采取的详细方案出了点问题,又或者是开拓者自己写代码的问题。
有时候架构模式之间也不是互斥的,也可以在不同场景下互补。
比如在MVC里,每个业务表现模块不一定要有三个元素齐聚,乃至也可以 VC-M-VC 共用一个M(可参考斯坦福iOS开拓课程内容)。

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

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

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

1)MVVM 和 MVW

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

官方对MVVM有这么一些描述:

V和VM是一对多的关系,即View 1:n ViewModel。
比较于 MVC里 Controller 1:n View,显然MVC的C更随意马虎膨胀,以是上面也提到了利用多Controller方案。
V引用VM,VM操作M,而VM不须要引用V(和MVP不同)。
VM作为V的高下文,包含V所需用来展示的数据,相应V被用户触发的事宜。
V对M无感知,反过来 M 对 V和VM 也无感知。
整体是疏松解耦的模式。
当VM的属性值发生变革时,通过数据绑定办法传达给V。
这就须要有机制支撑,有对应的机制MVC也能做数据绑定。

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

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

它的条件是有系统/平台/框架的机制支撑,不然实现本钱和繁芜度有点大。
比较MVC,它的上风是视图掌握逻辑不太会膨胀,代码单元更随意马虎被测试,在可读性、可掩护性上更好。
它可能带来的本钱/问题是入门上手较难,大略场景下利用随意马虎过度设计,繁芜场景下出问题调试比较麻烦。

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

由于业界有太多MV-开头的模式名词了,以是Angular官方直接声明了一个MVW:

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

这种声明有个好处,便是免去了谁对谁错/谁好谁坏的争执,而重点关注于谁适宜。

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

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

VIPER架构模式的运用

VIPER观点由View、Interactor、Presenter、Entity、Router几个元素组成,大致如下:

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

有一次,我在某个业务表现模块里运用VIPER,然后定义了一个XxxNetworkInteractor类,用来卖力做网络要求。

VIPER架构模式的演化

后来随着业务场景的变革,一个业务模块可能须要对接多套网关做事,XxxNetworkInteractor就要做抽象隔离,肃清业务逻辑对多套网关要求的感知,并且运用到其它业务模块。
这就有点像 VPER - I - VPER 模式 —— 多个业务模块对 Interactor 进行了复用。

进化

随着对接网关数的增加、网关能力的增强,XxxNetworkInteractor也配套培植了更多能力。
随着利用XxxNetworkInteractor的模块越来越多,不同业务的一些通用诉求和处理逻辑也随之增加。
随着XxxNetworkInteractor的功能越来越强大,代码越来越多,逐渐须要抽离出一个单独模块,不管是从提高编译速率,还是从封装暴露等角度。

于是,XxxNetworkSDK.framework出身了。

退化

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

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

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

那么,接手或卖力迁移复用的开拓者,会不会在想:为什么这些业务模块直接调用ATop,而没有利用一个XxxNetworkInteractor来做分层隔离呢?让业务不要感知详细的数据库或网络做事实现。

如果有,他只要做一件事情:让XxxNetworkInteractor对接XTop即可。
如果没有,他须要去更换修正每个业务模块的调用代码、返回值处理等等。
四 写在末了,回到问题

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

兵无常势,水无常形。
那么,回到想要办理的问题:

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

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

详细的方案履行上,由于因此可测性为主要关注点,再结合目前的技能方向,我会方向于采取MVVM。

MVVM的架构模式在理解和认知上比较成熟(该当是移动端开拓领域TOP2盛行的),便于履行和传播,并通过模式的名称来表示要强调的关注点:可测性。
MVVM可以更好地结合SwiftUI+Combine、Kotlin跨平台等技能方向。
MVVM完备版的上手门槛和大略场景的过度设计问题,可以通过采取不完备版MVVM来办理。
不完备版的MVVM可以cover适用于大略场景的MVC,近似于 超集-子集 关系。
虽然也可以通过多Controller的办法来办理MVC膨胀问题,但MVC的命名在实践中就随意马虎让程序员弱化掉可测性这个关注点(也可能是我个人理解和实践不足精确)。
繁芜场景的调试问题、更多可测性的实践,须要再摸索下,也希望得到干系分享和指示。

作者 | 思禽

原文链接:https://developer.aliyun.com/article/832823?utm_content=g_1000313179

本文为阿里云原创内容,未经许可不得转载。

标签:

相关文章

QQ聊天恶搞代码技术背后的趣味与风险

人们的生活越来越离不开社交软件。在我国,QQ作为一款历史悠久、用户众多的社交平台,深受广大网民喜爱。在QQ聊天的过程中,恶搞代码的...

SEO优化 2025-03-02 阅读1 评论0

Python代码截屏技术与应用的完美融合

计算机屏幕截图已经成为人们日常生活中不可或缺的一部分。无论是分享工作成果、记录游戏瞬间,还是保存网页信息,屏幕截图都发挥着重要作用...

SEO优化 2025-03-02 阅读1 评论0

QQ无限刷礼物代码技术突破还是道德沦丧

社交平台逐渐成为人们生活中不可或缺的一部分。QQ作为我国最具影响力的社交软件之一,其丰富的功能吸引了大量用户。近期有关QQ无限刷礼...

SEO优化 2025-03-02 阅读1 评论0