在开启一个新的项目的时候,最头痛的莫过于设计程序架构。我的 App 需要设计架构吗?答案是肯定的,原因在于,随着 App 不断的更新迭代,功能会越来越多,在添加功能的时候就会将代码都其在一起,会导致代码堆积,不易查找,结果便是开发进度越来越慢,更严重的可能导致无法继续迭代。
由此看来,应用的架构设计尤为重要,好的架构会使开发变得健壮,稳定以及拥有更快的开发进度。经过大量的实践,Android 中广为人知的设计模式有 MVC、MVP 和 MVVM。
那么今天我们来总结一下这些架构的结构,优缺点和使用场景。
MVC
在日常开发中,用户界面逻辑变化常常多余业务逻辑,PC 和 Web 开发者往往需要一种使用的分离用户界面的方法。MVC 模式解决了这一问题。
- Model:数据层,有管理业务逻辑和处理网络和数据 API 的能力。
- View:UI 层,从 Model 中获取数据,并将其可视化。
- Controller:逻辑层,接收用户的行为并且不要的时候更新 Model
因此,这个说明 Controller 和 View 依赖于 Model:Controller 更新数据,View 获取数据。但是,这对当时的 PC 和 web 开发者最为重要:Model 是分离的并且可独立于 UI 进行测试。后来,有一些变种的 MVC 出现了。众所周知的一种,是 Model 是否是被动的关系,或者 Model 主动相应它的变化
Android 中的 MVC
在 2011 年左右,当 Android 开始变得越来越流行的时候,架构问题就自然而然的出现了。在那个时候 MVC 是最受欢迎的 UI 模式之一,开发者们也尝试应用 MVC 到 Android 中。
如果你在 StackOverflow 上搜索像「如何在 Android 中应用 MVC」——在 Android 中最受欢迎问题之一,答案是一个 Activity 是 View 和 Controller 两个角色。回过头来看,这种声音简直令人惊讶!但是,在那个时候,主要的中心在于制作一个可测试的 Model,在 Android 平台上,通常 View 和 Controller 的实现选择取决于平台
应该如何在 Android 中应用 MVC
现今,如何应用 MVC 设计模式的问题有了答案,这个答案是比较容易找到的。所有的 Activity,Fragment 和控件应该是 MVC 世界中的 View。Controller 应该是单独的类,这个类不继承也不使用任何 Android 的类,Model 也是如此。
当连接 Controller 到 View 中时问题产生了,就是 Controller 需要告诉 View 更新。在被动模式的 MVC 架构中,Controller 需要持有一个 View 层的引用。考虑到测试的因素,最简单的做法是这样的,创建一个 BaseView 接口,Activity、Fragment 和控件都继承 BaseView。因此,Controller 要持有一个 BaseView 的引用。
优点
MVC 设计模式高度支持所关注的分离问题。这个优点不仅增加了代码的可测试性,而且更容易扩展,实现新功能相当简单。
Model 的所有类没有任何 Android 类的引用,并且便于单元测试。Controller 不继承也不实现 Android 任何类,并且持有 View 层的接口类型的引用。通过这种方式,Controller 单元测试也变得可能。
如果 View 层的控件都遵守单一职责原则,那么他们的角色只是为每个用户事件更新 Controller 和只是展示从 Model 获取的数据,Model 没有实现任何业务逻辑。在这种情况下,UI 测试应该尽可能覆盖 View 的功能。
缺点
View 层依赖于 Controller 层和 Model 层
在复杂的控件中,依赖 Model 的 View 开始产生负面影响。为了在 View 层中最小化实现逻辑,Model 层应该为每个可展示的元素提供可测试的方法。在主动 Model 实现中,类和方法的数量成倍增长,观察者需要为每个数据类型提供方法。
View 层强依赖 Controller 和 Model,在 UI 改变逻辑,需要在几个类中更新,降低了模式的灵活性。
MVP
以下是每个组件的角色:
- Model —— 数据层。处理业务逻辑和联系网络和数据库层的职责
- View —— UI 层。展示数据和接收 Presenter 中的用户操作。
- Presenter —— 从 Model 中获取数据,使用 UI 逻辑和管理 View 的状态,决定展示哪些内容以及相应从 View 发来的用户请求。
这样,View 和 Presenter 紧密的工作在一起,他们需要有另一方的引用。为了用 JUnit 对 Presenter 做单元测试,View 是抽象的并且是使用的是一个接口。Presenter 和它相符的 View 的关系是定义在 Contract 接口中,这样代码更具有可读性,两个层级联系更加清晰。
使用
Model
Model 工作在远程或本地数据资源,为了获取和保存数据。这是业务逻辑处理的地方。例如,当请求 Task 的列表时,Model 将尝试从本地数据获取它们。如果没有数据,它将访问网络,存储响应到本地资源,然后返回列表。
通过 RxJava 的帮助完成任务的获取:
|
|
Model 在本地和远程数据源的构造器接口中接收参数,使 Model 完全独立于任何Android 类,因此易于使用 JUnit 进行单元测试
View
View 和 Presenter 一起显示数据,并通知 Presenter 用户的操作。
所有 View 都实现相同的 BaseView 接口设置 Presenter。
|
|
View 在 onResume中通过调用 Presenter 的 subscribe
方法通知 Presenter 准备好进行更新。View 在 onPause 中调用 unsubscribe
告诉 Presenter,View 不在对更新感兴趣。如果 View 的实现是一个 Android 自定义控件,那么 subscribe
和 unsubscribe
方法就必须在 onAttachedToWindow
and onDetachedFromWindow
中调用。用户操作,像按钮点击,将触发 Presenter 中的相关类,这将决定接下来要发生什么事。
Presenter
Presenter 和 它的相关 View 通过 Activity 创建。
所有 Presenter 实现相同的 BasePresenter 接口。
|
|
当 subscribe
方法被调用,Presenter 开始向 Model 层请求数据,然后将接收到的数据应用到 UI 逻辑中并且设置到 View 中。
优点
- 降低耦合度
- 模块指责划分
- 利于测试驱动开发
- 代码复用
- 隐藏数据
- 代码灵活
缺点
相比于 MVC 需要增加很多接口,因此比较适合较小的项目。
MVVM
在 MVVM 设计模式中的主要角色,如下所示:
- View:通知 ViewModel 关于用户的操作
- ViewModel:暴露数据流关联 View
- DataModel:抽象数据源。ViewModel 和 DataModel 一起获取和保存数据。
乍一看,MVVM 和 MVP 设计模式很像,因为它们俩在抽象 View 的状态和行为都做的很出色。Presenter 模型抽象了一个独立于指定用户界面平台的 View,MVVM 模式创建了简化事件驱动的用户界面编程。
如果 MVP 设计模式意味着 Presenter 直接告诉 View 索要展示的内容,那么在 MVVM 中,ViewModel 暴露了 View 可以绑定的事件流。这样,ViewModel 不需要持有 View 的引用。这意味着所有 MVP 设计模式中需要的所有接口,都可以删掉了。
View 也通知 ViewModel 不同的操作。MVVM 设计模式支持在 View 和 ViewModel 双向绑定,并且在 View 和 ViewModel 之间有多对一的关系。View 有 ViewModel 的引用,但 ViewModel 没有 View 的信息。数据的消费者应该知道生产者,但生产者 —— ViewModel —— 不知道也不关心消费的数据。