Android开发中的MVP架构

原文链接:A useful stack on android #1, architecture

原文作者:Saúl Molinero

译文出自:小鄧子的简书

译者: 小鄧子

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

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

为什么越来越多的人开始关注架构?

首先,MVP是什么?

哪种架构才是最好的,MVC,MVVM还是MVP?

MVP的利与弊

Show me the code!!!代码展示

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

详细生动的代码示例

如何编写测试代码

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

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

(译者注:阅读更多请点击原作者PPT

介绍~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”描述了依赖的规则是什么。

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

这是上面片文章的摘要:

Entities:

可以是一个持有方法函数的对象

可以是一组数据结构或方法函数

它并不重要,能在项目中被不同应用程序使用即可

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定义接口。

/**

* Interface classes for the Top view

*/publicinterfaceTopView{/**

* Initialize the view.

*

* e.g. the facade-pattern method for handling all Actionbar settings

*/voidinitViews();/**

* Open {@link DatePickerDialog}

*/voidopenDatePickerDialog();/**

* Start ListActivity

*/voidstartListActivity();}

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

TopActivity只是负责处理事件监听或者展示每个视图组件

所有的业务逻辑必须委托给Presenter类

在MVP中,View和Presenter是一 一对应的(在MVVM中是一对多的)

publicclassTopActivityextendsActivityimplementsTopView{// here we use ButterKnife to inject views/**

* Calendar Title

*/@Bind(R.id.calendar_title)  TextView mCalendarTitle;privateTopPresenter mTopPresenter;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);      setContentView(R.layout.activity_top);      ButterKnife.bind(this);// Save TopPresenter instance in a meber variable fieldmTopPresenter =newTopPresenter();      mTopPresenter.onCreate(this);  }/*

* Overrides method from the {@link TopView} interfaces

*/@OverridepublicvoidinitViews(){// Actionbar settins// set event listeners}@OverridepublicvoidopenDatePickerDialog(){      DatePickerFragment.newInstance().show(getSupportFragmentManager(),              DatePickerFragment.TAG);// do not write logic here... all logic must be passed to the PresentermTopPresenter.updateCalendarDate();  }@OverridepublicvoidstartListActivity(){      startActivity(newIntent(this, ListActivity.class));  }}

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

ublicclassTopPresenter{@NullableprivateTopView mView;privateTopUseCase mUseCase;publicTopPresenter(){      mUseCase =newTopUseCase();    }publicvoidonCreate(@NonNull TopView topView){        mView = topView;// here you call View's implemented methodsmView.initViews();    }publicvoidupdateCalendarDate(){// do not forget to return if view instances is nullif(mView ==null) {return;        }// here logic comesString dateToDisplay = mUseCase.getDateToDisplay(mContext.getResources());        mView.updateCalendarDate(dateToDisplay);// here you save date, and this logic is hidden in UseCase classmUseCase.saveCalendarDate();    }}

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

总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容