Android开发中的MVP架构

简介:

最近越来越多的人开始谈论架构。我周围的同事和工程师也是如此。尽管我还不是特别深入理解MVP和DDD,但是我们的新项目还是决定通过MVP来构建。

这篇文章是我通过研究和学习各种文章以及专题讨论所总结出来的,它包括以下几点:

  • 为什么越来越多的人开始关注架构?
  • 首先,MVP是什么?
  • 哪种架构才是最好的,MVC,MVVM还是MVP?
  • MVP的利与弊
  • Show me the code!!!代码展示

不幸的,这篇文章将不包括:

  • 详细生动的代码示例
  • 如何编写测试代码

最后,我将告诉你如何更进一步学习这些专题。

顺便提一下,我于上周在当地的一个研讨会上对MVP架构进行了相关演讲。这篇文章与当时的演讲内容相差无几。

介绍~Activity是上帝类~

首先,让我们思考一下为什么在Android开发中如此迫切地需要一个清晰的软件架构。

该段摘自“代码大全第二版”:

避免创建神类。避免创建无所不知,无所不能的上帝类。如果一个类需要花费时间从其他类中通过Get()和Set()检索数据(也就是说,需要深入业务并且告诉它们如何去做),所以是否应该把这些功能函数更好的组织到其它类而不是上帝类中。(Riel 1996)

上帝类的维护成本很高,你很难理解正在进行的操作,并且难以测试和扩展,这就是为什么要避免创建上帝类的黄金法则。

然而,在Android开发中,如果你不考虑架构的话,Activity类往往会越来越大。这是因为,在Android中,允许View和其它线程共存于Activity内。其实最大的问题莫过于在Activity中同时存在业务逻辑和UI逻辑。这会增加测试和维护的成本。

Activity是上帝

这是为什么需要清晰架构的原因之一。不仅会造成Activity的臃肿,还会引起其他问题,如使Activity和Fragment的生命周期变复杂,以及数据绑定等。

什么是MVP?

MVP代表Model,View和Presenter。

  • View层负责处理用户事件和视图部分的展示。在Android中,它可能是Activity或者Fragment类。
  • Model层负责访问数据。数据可以是远端的Server API,本地数据库或者SharedPreference等。
  • Presenter层是连接(或适配)View和Model的桥梁。

下图是基于MVP架构的模式之一。View是UI线程。Presenter是View与Model之间的适配器。UseCase或者Domain在Model层中,负责从实体获取或载入数据。依赖规则如下:

The Dependency Injection

关键是,高层接口不知道底层接口的细节,或者更准确地说,高层接口不能,不应该,并且必须不了解底层接口的细节,是(面向)抽象的,并且是细节隐藏的。

The higher interfaces do not know about the details of the lower ones

依赖规则?

Uncle Bob的“The Clean Architecture”描述了依赖的规则是什么。

同心圆将软件划分为不同的区域,一般的,随着层级的深入,软件的等级也就越高。外圆是实现机制,内圆是核心策略。

这是上面片文章的摘要:

Enitities:

  • 可以是一个持有方法函数的对象
  • 可以是一组数据结构或方法函数
  • 它并不重要,能在项目中被不同应用程序使用即可

Use Cases

  • 包含特定于应用程序的业务规则
  • 精心编排流入Entity或从Entity流出的数据
  • 指挥Entity直接使用项目范围内的业务规则,从而实现Use Case的目标

Presenters Controllers

  • 将Use Case和Entity中的数据转换成格式最方便的数据
  • 外部系统,如数据库或网页能够方便的使用这些数据
  • 完全包含GUI的MVC架构

External Interfaces, UI, DB

  • 所有的细节所在
  • 如数据库细节,Web框架细节,等等

MVC,MVP还是MVVM?

那么,哪一个才是最好的呢?哪一个比其他的更优秀呢?我能只选择一个吗?

答案是,NO。

这些模式的动机都是一样的。那就是如何避免复杂混乱的代码,让执行单元测试变得容易,创造高质量应用程序。就这样。

当然,远不止这三种架构模式。而且任何一种模式都不可能是银弹,他们只是架构模式之一,不是解决问题的唯一途径。这些只是方法、手段而不是目的、目标。

利与弊

OK,让我们回到MVP架构上。刚刚我们了解了什么是MVP,讨论了MVP以及其它热门架构,并且介绍了MVC,MVP和MVVM三者间的不同。这是关于MVP架构利与弊的总结:

**利

  • 可测试(TDD)
  • 可维护(代码复用)
  • 容易Reviewe
  • 信息隐蔽

**弊

  • 冗余的,尤其是小型App开发
  • (有可能)额外的学习曲线
  • 开始编写代码之前需要时间成本(但是我敢打赌,设计架构是所有项目开发所必需的)

show me the code!!!

这里仅展示了MVP模式的一小段结构。如果你想了解更多项目或生动的代码示例,请参考文章末尾的“链接和资源”。那里有非常丰富和设计巧妙的示例,基本都托管在Github上,以便你能clone,在设备上运行,并了解工作原理。

首先,为每一个View定义接口。

 
 
  1. /** 
  2.  * Interface classes for the Top view 
  3.  */ 
  4. public interface TopView { 
  5.   
  6.     /** 
  7.      * Initialize the view
  8.      *  
  9.      * e.g. the facade-pattern method for handling all Actionbar settings 
  10.      */ 
  11.     void initViews(); 
  12.   
  13.     /** 
  14.      * Open {<a href="http://www.jobbole.com/members/57845349">@link</a> DatePickerDialog} 
  15.      */ 
  16.     void openDatePickerDialog(); 
  17.   
  18.     /** 
  19.      * Start ListActivity 
  20.      */ 
  21.     void startListActivity(); 
  22.  

让我们重写TopView类,要点如下:

  • TopActivity只是负责处理事件监听或者展示每个视图组件
  • 所有的业务逻辑必须委托给Presenter类
  • 在MVP中,View和Presenter是一 一对应的(在MVVM中是一对多的) 
 
 
  1. public class TopActivity extends Activity implements TopView { 
  2.   
  3.   // here we use ButterKnife to inject views 
  4.   /** 
  5.    * Calendar Title 
  6.    */ 
  7.   @Bind(R.id.calendar_title) 
  8.   TextView mCalendarTitle; 
  9.   
  10.   private TopPresenter mTopPresenter; 
  11.   
  12.   @Override 
  13.   protected void onCreate(Bundle savedInstanceState) { 
  14.       super.onCreate(savedInstanceState); 
  15.       setContentView(R.layout.activity_top); 
  16.       ButterKnife.bind(this); 
  17.   
  18.       // Save TopPresenter instance in a meber variable field 
  19.       mTopPresenter = new TopPresenter(); 
  20.       mTopPresenter.onCreate(this); 
  21.   } 
  22.   
  23.   /* 
  24.    * Overrides method from the {<a href="http://www.jobbole.com/members/57845349">@link</a> TopView} interfaces 
  25.    */ 
  26.   
  27.   @Override 
  28.   public void initViews() { 
  29.       // Actionbar settins 
  30.   
  31.       // set event listeners 
  32.   } 
  33.   
  34.   @Override 
  35.   public void openDatePickerDialog() { 
  36.       DatePickerFragment.newInstance().show(getSupportFragmentManager(), 
  37.               DatePickerFragment.TAG); 
  38.   
  39.       // do not write logic here... all logic must be passed to the Presenter 
  40.       mTopPresenter.updateCalendarDate(); 
  41.   } 
  42.   
  43.   @Override 
  44.   public void startListActivity() { 
  45.       startActivity(new Intent(this, ListActivity.class)); 
  46.   } 
  47.  

这是Presenter类,最重要的一点是Presenter仅仅是连接View与Model的适配桥梁。比如,TopUseCase#saveCalendarDate()是对TopPresenter细节隐藏的,同样对TopView也是如此。你不需要关心数据结构,也不需要关心业务逻辑是如何工作的。因此你可以对TopUseCase执行单元测试,因为业务逻辑与视图层是分离的。

 
 
  1. public class TopPresenter { 
  2.   
  3.     @Nullable 
  4.     private TopView mView; 
  5.   
  6.     private TopUseCase mUseCase; 
  7.   
  8.     public TopPresenter() { 
  9.       mUseCase = new TopUseCase(); 
  10.     } 
  11.   
  12.     public void onCreate(@NonNull TopView topView) { 
  13.         mView = topView; 
  14.   
  15.         // here you call View's implemented methods 
  16.         mView.initViews(); 
  17.     } 
  18.   
  19.     public void updateCalendarDate() { 
  20.         // do not forget to return if view instances is null 
  21.         if (mView == null) { 
  22.             return
  23.         } 
  24.   
  25.         // here logic comes 
  26.         String dateToDisplay = mUseCase.getDateToDisplay(mContext.getResources()); 
  27.         mView.updateCalendarDate(dateToDisplay); 
  28.   
  29.         // here you save dateand this logic is hidden in UseCase class 
  30.         mUseCase.saveCalendarDate(); 
  31.     } 
  32.  

当然,尽管业务逻辑被实现在Activity类中,你依然可以执行单元测试,只不过这会耗费很多时间,而且有些复杂。可能需要更多的时间来运行App,相反,你本应该充分利用测试类库的性能,如Robolectric。

总结

这里没有万能药,而且MVP也仅仅是解决方案之一,它可以与其他方法协同使用,同样,也可以有选择的用于不同项目。




本文作者:佚名
来源:51CTO
目录
相关文章
|
11天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
18 0
|
21天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
25天前
|
负载均衡 测试技术 持续交付
高效后端开发实践:构建可扩展的微服务架构
在当今快速发展的互联网时代,后端开发扮演着至关重要的角色。本文将重点探讨如何构建可扩展的微服务架构,以及在后端开发中提高效率的一些实践方法。通过合理的架构设计和技术选型,我们可以更好地应对日益复杂的业务需求,实现高效可靠的后端系统。
|
25天前
|
机器学习/深度学习 自然语言处理 并行计算
大模型开发:什么是Transformer架构及其重要性?
Transformer模型革新了NLP,以其高效的并行计算和自注意力机制解决了长距离依赖问题。从机器翻译到各种NLP任务,Transformer展现出卓越性能,其编码器-解码器结构结合自注意力层和前馈网络,实现高效训练。此架构已成为领域内重要里程碑。
27 2
|
20天前
|
监控 Java 开发者
构建高效微服务架构:后端开发的新范式
在数字化转型的浪潮中,微服务架构以其灵活性、可扩展性和容错性成为企业技术战略的关键组成部分。本文深入探讨了微服务的核心概念,包括其设计原则、技术栈选择以及与容器化和编排技术的融合。通过实际案例分析,展示了如何利用微服务架构提升系统性能,实现快速迭代部署,并通过服务的解耦来提高整体系统的可靠性。
|
21天前
|
NoSQL Java Redis
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(二)
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件
14 0
|
5天前
|
监控 负载均衡 API
构建高性能微服务架构:后端开发的最佳实践
【4月更文挑战第14天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了后端开发人员在设计和维护高性能微服务时需要遵循的一系列最佳实践。我们将从服务划分原则、容器化部署、API网关使用、负载均衡、服务监控与故障恢复等方面展开讨论,并结合实际案例分析如何优化微服务性能及可靠性。通过本文的阅读,读者将获得实施高效微服务架构的实用知识与策略。
|
12天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
14天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
19 1
Android开发之使用OpenGL实现翻书动画
|
14天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
12 1
Android开发之OpenGL的画笔工具GL10