最近在实验室内部做了一次分享,主要内容是Android开发中常使用的三种软件框架模式,MVCMVPMVVM,另外写了一个小Demo来演示这几种模式的实际写法,在此一并发到博客上。

要声明的是,对于软件框架模式,每个人有自己的理解,另外模式也有多种变形,可能会与本文中的不完全一致,因此本文阐述的是我自己对这几个模式的理解。

软件框架模式

什么是软件框架模式

软件框架模式是软件架构的设计理念,一个通用的、可重用的解决方案,用于解决在给定上下文中的软件体系结构问题

软件框架模式即为软件的结构设计,对于一个特定场景下的问题,如何组织和编写软件解决这个问题。就像人类要组织一场活动,如何安排场地和人员以完成活动就是活动的结构设计。

良好的软件框架模式意味着优秀的软件架构,一般具有以下几个优点:

  • 框架中各部分模块关系清晰,职责和功能划分明确,功能模块易于复用,降低开发难度
  • 每个模块和其他模块之间关联度低,便于单元测试
  • 耦合度低,程序灵活,应对新变化如新增需求或者修改需求时便于软件维护

很多人会把软件框架模式和软件设计模式混淆,个人认为,这两者都是软件从业人员经验的总结。但相对而言,软件设计模式更像是编程过程中局部使用的编程技巧,而框架模式则负责整个软件框架,处于全局的视角位置。

常见的软件框架模式

分层模式

分层模式示意图
分层模式示意图

「分层模式」是最常见的软件框架模式,很多组织架构和这种模式十分相似。该模式将软件分成若干个水平层,每一层都有特定的角色和职能,代表着应用的某一些功能,不需要知道其他层的细节,层与层之间通过接口通信,OSI的七层和TCP/IP的5层协议都应用了这种模式。

事件总线模式

事件总线模式示意图
事件总线模式示意图

「事件总线模式」主要用于处理事件,包括4个主要组件:事件源、事件监听器、通道和事件总线。对于不同事件源,产生的事件不同,这些事件都会发布到事件总线上的特定通道上。事件监听器器订阅特定的通道,每当有新事件发生时,订阅了该事件的监听器就可以通过订阅的通道得知该事件,从而做一定处理,每个监听器也可以对多个不同的事件进行监听。事件总线模式和设计模式里的发布订阅模式有一定相似之处。

微核架构

微核架构示意图
微核架构示意图

「微核架构」也称为插件化应用模式,由薄核心和多个功能插件组成。这种模式可以通过插件的形式添加额外的特性到核心系统中,提供了很好的扩展性,也使得新特性与核心系统隔离开。

一般来说,核心提供了特定场景下的通用业务逻辑流程,而插件模块则根据这些规则实现具体的业务逻辑,一个例子是FreeSwitchFreeSwitch是一个软电话交换系统,提供了软电话的通用逻辑,如话机两端先进行信令的沟通然后通过这个流程,详细的信令协议则由各插件实现,如SIP协议,H323协议等等,还有语音编解码的具体过程,都是由各语音编码插件完成,FreeSwitch只是提供了整体处理流程。

微服务架构

「微服务架构」提倡将大而全的应用功能拆分成以服务组件为单位的小模块,每个服务组件的服务能力粒度有大有小,小到一个单一的字符串提取,大到一个完整的计费系统。5G目前使用的就是微服务架构,将运营商的多种功能拆分成小功能单元,每一个新的应用都可以调用不同能力的服务组件以实现自身的功能。通信也在往互联网软件的方向发展。

模型-视图-控制器模式

MVC模式示意图
MVC模式示意图

「模型-视图-控制器模式」也即「MVC模式」。这个模式多用于GUI程序的开发,如前端、iOSAndroidMVC模式将软件分成三个层级,模型(Model)、视图(View)和控制层(Controller)。模型包含核心功能和数据,视图展示信息,控制器处理用户输入。这个模式也是本篇文章的主要内容。

MVC模式

MVC模式介绍

MVC模式主要分为三个部分,ModelViewController

  • 模型持有所有的数据、状态和程序逻辑,一个模型可以有不同的多个视图表现形式,复用性高。
  • 视图是用户看到并与之交互的页面,通常直接从模型中取得它需要显示的状态与数据,一个视图往往有一个相应的控制器,理论上也可以同不同的模型相关联
  • 控制器位于视图和模型中间,负责接受用户的输入,将输入进行解析并反馈给模型

下图是MVC模式的一个流程示例。View层有用户点击,Controller层接收到点击事件,做简单处理之后向Model层发送请求,Model层中主要的程序逻辑对接收到的数据进行计算等操作,得到结果后再通知View层进行视图更新。

MVC流程
MVC流程

以一个计算器为例,如果要用MVC模式设计软件,则View层负责设计和绘制界面上的数字和运算符号按钮,Controller层响应点击事件,处理视图层传递的数据,并将数据传递给Model层,Model层通过控制层得到输入的数字和运算符,并对数据进行计算,最终将计算结果传递给View层进行视图更新。

计算器
计算器

当用户点击7 * 9这三个按钮时,Controller层接收到这三个按钮的点击事件,然后将这三个按钮对应的数字和运算符传递给Model层进行计算,Model层计算得到最终结果为63,随之将63这个数字传递给View层进行展示。

这就是MVC模式在计算器这个软件中的应用。这样分离之后,整个软件耦合度大大降低,软件修改和功能复用方便了很多。如果对计算器界面不满意,那么只需要修改View重新设计界面;如果想在其他平台如Android平台复用,Model包含了主要的程序逻辑,直接将Model层复用即可,Android端只需要重写View层和Controller层即可,整个软件灵活性大大增强。

在Android开发中的应用

Android开发其实天生就是MVC模式,如下图所示:

Android开发对应各层
Android开发对应各层

其中,XML层作为View层,定义视图的层级;Activity作为Controller层,接收视图的点击等效果,然后将数据请求等发送给Model层;Model层负责从网络或者数据库中获取数据。以实现下图的效果为例,界面上有一个按钮,点击按钮显示一条Toast,具体显示的消息从远端获取。

示意图
示意图

整个流程过程如下图所示,View层控制页面布局,Controller层收到点击事件,处理之后向Model层获取数据,Model层获取数据,获取成功通知View层更新视图。

流程图
流程图

具体实现中,涉及到通知方式的问题,Android中有很多方法可以解决这个问题,这里使用接口回调。

首先定义回调接口StringCallback,里面有一个回调方法。

回调接口代码
回调接口代码

XML定义按钮等页面布局。

XML布局代码
XML布局代码

Activity实现了回调接口StringCallbcak,显示Toast消息,同时监听按钮的点击事件,当按钮被点击的时候,向Model层获取数据,同时将回调对象传入。

Activity布局代码
Activity布局代码

Model层向远端获取数据,这里为了方便起见,直接使用固定的字符串。数据获取获取成功后,通知View层更新视图,使用传入的回调对象进行操作。

Model层代码
Model层代码

MVC模式在Android中实现大概就是这个框架。不过实现中存在一个问题,Model层实际应该向View通知更新视图,但在这里,通知的接受者实际上是Activity,也就是Controller。这么做的原因在于,XML对视图的控制能力是在太弱,只能写一些静态的视图布局,动态的添加和修改等操作还得Activity完成。因此,这里的框架更像是下图所展示的模式:

实际的模式
实际的模式

这样的话,业务逻辑都放到了Model层,Model层已经解耦,但是Controller层承包了View层的很多工作,耦合性依然存在。因此,出现了MVC模式的演化版本,MVP模式。

MVP模式

MVP模式介绍

MVC类似,MVP模式也主要分为三个部分,ModelViewPresenter

  • 模型持有所有的数据、状态和程序逻辑,一个模型可以有不同的多个视图表现形式,复用性高。
  • 视图是用户看到并与之交互的页面,一个视图往往有一个相应的表示层,理论上也可以同不同的模型相关联
  • 表示层位于视图和模型中间,负责接受用户的输入,将输入进行解析并反馈给模型

下图是MVC模式的一个流程示例。View层有用户点击,Presenter层接收到点击事件,做简单处理之后向Model层发送请求,Model层中主要的程序逻辑对接收到的数据进行计算等操作,得到结果后再通知Presenter层,随后Presenter再通知View层进行视图更新。

MVP模式
MVP模式

以一个计算器为例,如果要用MVP模式设计软件,则View层负责设计和绘制界面上的数字和运算符号按钮,Presenter层响应点击事件,处理视图层传递的数据,并将数据传递给Model层,Model层通过控制层得到输入的数字和运算符,并对数据进行计算,最终将计算结果传递给Presenter层,Presenter层接收到数据后,再通知View层进行视图更新。在某种意义上来说,MVP和代理模式有些类似。

计算器
计算器

当用户点击7 * 9这三个按钮时,Presenter层接收到这三个按钮的点击事件,然后将这三个按钮对应的数字和运算符传递给Model层进行计算,Model层计算得到最终结果为63,随之将63这个数字传递给Presenter层,Presenter层接收到结果,通知View层进行展示。

这就是MVP模式在计算器这个软件中的应用,这个模式和MVC模式最大的区别在于,Model层和View层不能直接通信,传递数据等操作都需要Presenter层介入才能完成。

在Android开发中的应用

以实现和MVC模式中相同的界面布局为例,界面上有一个按钮,点击按钮显示一条Toast,具体显示的消息从远端获取。

示意图
示意图

整个流程过程如下图所示,View层控制页面布局,Controller层收到点击事件,处理之后向Model层获取数据,Model层获取数据,获取成功通知View层更新视图。

流程图
流程图

具体实现中,涉及到通知方式的问题,Android中有很多方法可以解决这个问题,这里使用接口回调。在MVP模式的实现中,一共有两次回调用以通知,分别是Model层通知Presenter层和Presenter层通知View层。因此,一共需要定义两个回调接口,分别由Presenter层和View层来实现。

首先定义两个回调接口,里面有两个方法。

View层接口](17.jpg) ![Presenter层接口
View层接口](17.jpg) ![Presenter层接口

XML部分代码与MVC中一致,在这个实现中,ActivityXML共称为View层,XML负责静态,Activity负责动态,同时需要实现View层接口,并在接收到按钮点击事件时调用Presenter层的功能。

Activity代码
Activity代码

Presenter层实现了一个通过Model层获取数据的方法,同时需要实现Presenter层接口,供Model层回调。

Presenter代码
Presenter代码

Model层向远端获取数据,这里为了方便起见,直接使用固定的字符串。数据获取获取成功后,通知Presenter层更新数据,使用传入的回调对象进行操作。Presenter层接收到通知后,也通过传入的View层回调对象进行操作。

Model层代码
Model层代码

示例所实现的功能太少,所以现在看来代码略显繁琐,但是代码层次分明,便于扩展。

其实,无论是MVC还是MVP模式,都有一个很重要的功能实现是将新的数据(PresenterController层)更新到视图(View层)上。如果有框架能够自动完成这个工作,那么能节省很大一部分工作,开发者可以专注于数据更新和逻辑处理。因此,出现了MVVM模式。

MVVM模式

MVVM模式介绍

MVVM模式则是将MVP模式中的Presenter改成了ViewModel层,负责的功能类似,区别在于与View层的双向绑定。

下图是MVVM模式的一个流程示例。View层有用户点击,VM层通过双向绑定得知,做简单处理之后向Model层发送请求,Model层中主要的程序逻辑对接收到的数据进行计算等操作,得到结果后再通知VM层。VM数据更改之后,View层通过双向绑定,从而得知数据更新,并进行视图更新。

MVVM模式
MVVM模式

MVVM模式一般很少会自己实现,有现成的框架。前端有Vue.js,是一个提供了MVVM模式中数据双向绑定的Javascript库。Android方面,Google提供了Databinding库。

DataBinding

DataBinding是谷歌官方发布的一个框架,顾名思义即为数据绑定,是MVVM模式在Android上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。

DataBinding其实并没有实现新的API来完成双向绑定,只是对原有API的封装,如findViewById()setText(),亦或是click事件,这些都被框架隐藏起来了。

至于这些事件的触发,例如View层接收到点击事件并通知ViewModel层,则是使用发布订阅模式实现的,如下图:

DataBinding实现
DataBinding实现

每当View层更改时,ViewModel层通过订阅收到通知;每当ViewModel层更改时,View层也会通过订阅收到通知,这样就实现了双向绑定。

这里可能会出现一个循环问题,假设其中某一层A改变,那么另外一层B得到通知随之改变,这会导致A层得到通知,A层也改变,循环往复出现问题。

DataBinding通过前后数据对比来决定是否更新,假设View层需要更新文本信息(如TextView),则DataBinding会对比当前的文本和更新的文本,如果不一致则进行更新操作;如果相同,则不做任何操作。其他的控件也有类似的判定操作,这样就解决了数据双向绑定中的循环问题。

在Android开发中的应用

以实现和MVP模式中相同的界面布局为例,界面上有一个按钮,点击按钮显示一条Toast,具体显示的消息从远端获取。

示意图
示意图

整个流程过程如下图所示。

流程图
流程图

DataBinding因为有现成的框架,因此这里就不贴具体实现的代码了。

总结

安卓开发中MVC模式存在的耦合问题在其他开发中可能不存在,各个模式之间也没有什么优劣之分,只有适合与否的问题。整体而言,在不考虑框架的前提下,安卓开发中个人使用MVP模式较多。