Android开发中的MVP架构
这篇文章背后的故事
最近,越来越多的Android开发人员似乎在讨论架构。我周围的同事和工程师也是如此。虽然我对MVP和DDD不太了解,但我们的新项目决定以MVP架构为基础。
这个帖子是我从各种职位和讨论中研究和学习的,其中包括:
为什么越来越多的人在谈论这个架构?
什么是MVP,首先是什么?
MVC,MVVM或MVP?哪一个是最好的?
MVP的利与弊
上代码!
还有,这是这篇文章不包括的内容:
- 详细的可运行的样例代码
- 如何编写测试代码
文章的最后,我会告诉你进一步阅读这些主题
顺便说一下,我上周在一个关于MVP架构的地方研讨会上发表了讲话。这个帖子也是为了与演讲对应。
介绍〜Activity是上帝〜
首先,让我们来谈谈Android开发提出简洁架构的必要性的根本原因。
以下是“Code Complete 2nd Edition”的摘录:
Avoid creating god classes Avoid creating omniscient classes that are all-knowing and all-powerful. If a class spends its time retrieving data from other classes using Get() and Set() routines ( that is, digging into their business and telling them what to do ), ask whether that functionality might better be organized into those other classes rather than into the god class ( Riel 1996 ).
God Class很难维护,了解发生了什么,执行单元测试,扩展阶段等等。这是避免创造god classes的黄金规则。
然而,在Android开发中,如果您不太在意架构,那么Activity类往往会越来越大.这是因为在Android中,View和其他线程可以在Activity类中共存。最大的问题?业务逻辑和UI逻辑在Activity类中共存。 这导致了单元测试或维护性的困难。
这是需要简洁架构的原因之一。不仅活动类的扩展,还有其他问题,如Activity和Fragment类中的复杂生命周期,数据绑定等。
什么是MVP?
MVP代表Model, View, and Presenter.
View 是一个处理所有用户操作并显示每个视图部分的图层。在Android上,这可以是Activity和Fragment类。
Model是负责数据访问的层。数据是从哪里来的。例如远程服务器API,本地数据库,如SQL,SharedPreferences等。
Presenter是在View和Model模式之间桥接(或适配器)数据的层。
关键是,较高的接口不了解较低的接口,或者更准确地说,较高的接口不能,不应该也不能知道较低的接口的细节。是的,信息隐藏。
依赖规则?
Uncle Bob的 “The Clean Architecture” 对于依赖规则是非常有用的。
The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.
以下是上述帖子的摘录:
Entities
- 可以是一个带有方法的对象
- 可以是一组数据结构和功能
- 只要这些实体可以被企业中的许多不同的应用程序使用就是没问题的。
Use Cases 用例
- 包含应用程序的业务规则
- 协调数据流往实体的流程
- 指导这些实体使用企业范围的业务规则来实现用例的目标
Presenters, Controllers
- 从用例和实体最方便的格式转换数据,
- 转换到一些外部代理(如DB或Web)最方便的格式
- 完全包含一个GUI的MVC架构
外部接口,UI,DB
- 所有的细节都在哪里
- 如DB,Web框架等。
MVC,MVP还是MVVM?
那么哪一个是最好的?哪一个优于其他人?我应该选择其他中的唯一一个的吗?
答案是 不。
这些模式的动机是一样的。如何避免复杂的结构混乱的代码,让您轻松执行单元测试,并创建更高品质的应用程序。而已。
当然,除了三种以外,还会有更多的模式。他们中的每一个都不是杀手锏,也不是唯一的答案。它们是方法论之一。解决问题的一个方法。不要把手段变成目的。
利弊
好的,让我们回到MVP架构。刚才,我们已经看到了MVP是什么,现在我们趁热打铁来讨论MVP或其他体系结构,以及MVC,MVP和MVVM之间的区别。
优点
- 可测试(导致TDD)
- 可维护(代码重用)
- 容易得到审查
- 信息隐藏
缺点
- 冗长,特别是当应用程序大小小的时候
- 额外的学习曲线(也许)
- 在开始编码之前需要时间(但我敢打赌,架构是所有开发必须的步骤)
口说无凭,代码为证
这里只显示MVP模式的最小结构。如果您想看到更多的例子或活泼的例子,请参考最后的“链接和资源”一章。这里只显示MVP模式的最小结构。如果您想看到更多的例子或活泼的例子,请参考最后的“链接和资源”一章。有更多丰富和精心设计的示例,基本上托管在Github中,以便您可以克隆并查看其在设备上的工作原理。
首先,我们定义每个视图的接口。
/**
* Interface classes for the Top view
*/
public interface TopView {
/**
* Initialize the view.
*
* e.g. the facade-pattern method for handling all Actionbar settings
*/
void initViews();
/**
* Open {@link DatePickerDialog}
*/
void openDatePickerDialog();
/**
* Start ListActivity
*/
void startListActivity();
}
让我们覆盖TopView类。这里的关键是:
- TopActivity仅处理事件侦听器或显示每个视图部分
- 必须将所有业务逻辑委派给Presenter类
- 在MVP中,View和Presenter类被声明为1对1(在MVVM中,在某些方面为1)
public class TopActivity extends Activity implements TopView {
// here we use ButterKnife to inject views
/**
* Calendar Title
*/
@Bind(R.id.calendar_title)
TextView mCalendarTitle;
private TopPresenter mTopPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top);
ButterKnife.bind(this);
// Save TopPresenter instance in a meber variable field
mTopPresenter = new TopPresenter();
mTopPresenter.onCreate(this);
}
/*
* Overrides method from the {@link TopView} interfaces
*/
@Override
public void initViews() {
// Actionbar settins
// set event listeners
}
@Override
public void openDatePickerDialog() {
DatePickerFragment.newInstance().show(getSupportFragmentManager(),
DatePickerFragment.TAG);
// do not write logic here... all logic must be passed to the Presenter
mTopPresenter.updateCalendarDate();
}
@Override
public void startListActivity() {
startActivity(new Intent(this, ListActivity.class));
}
}
这是Presenter类。最重要的是,presenter 只能在模型和视图之间使用适配器。例如,TopPresenter隐藏了“TopUseCase#saveCalendarDate()”,TopView也是如此。您不必关心数据结构是什么,以及业务逻辑如何工作。因此,您可以执行TopUseCase的单元测试,因为业务逻辑与View层分离。
public class TopPresenter {
@Nullable
private TopView mView;
private TopUseCase mUseCase;
public TopPresenter() {
mUseCase = new TopUseCase();
}
public void onCreate(@NonNull TopView topView) {
mView = topView;
// here you call View's implemented methods
mView.initViews();
}
public void updateCalendarDate() {
// do not forget to return if view instances is null
if (mView == null) {
return;
}
// here logic comes
String dateToDisplay = mUseCase.getDateToDisplay(mContext.getResources());
mView.updateCalendarDate(dateToDisplay);
// here you save date, and this logic is hidden in UseCase class
mUseCase.saveCalendarDate();
}
}
是的,您当然可以执行单元测试,即使业务逻辑在Activity类中实现,但这需要更多的时间和复杂性。运行应用程序可能需要更多时间。相反,您应该充分利用单元测试库的力量,如Robolectric。
结论
没有杀手锏,而MVP本身就是其中之一。它可以与其他方法混合使用,同时也可以选择性地用于每个项目。
链接和资源
Uncle Bob 的 The Clean Architecture
这是 Uncle Bob的帖子,描述依赖规则是什么,以及每个组件之间的工作原理。我开始说的图形是受他的帖子的启发。虽然这并不是一直以Android开发为重点,但他的话语意味深长并且组织的也不错。必须读一个。
Fernando Cejas 的Architecting Android…The clean way?
我认为这是最着名和最受欢迎的博客文章,解释如何将MVP架构纳入Android开发。我也从他易于阅读和写得很好的博客文章中看到了“MVP”这个词。每个想要了解MVP架构如何在真实应用程序中工作的Android开发人员,他的Github示例代码应该被克隆。
Thanos Karpouzis 的 Android Architecture
MVC,MVP和MVVM在Android项目上的简单指南。我从他平凡但广泛覆盖的帖子中学到了很多,特别是MVC,MVP和MVVM之间的差异。
Software Design patterns on Android English
这是Karumi的高级Android开发人员的一个演讲,它从一些设计模式(例如Renderer Pattern,Repository Pattern and Command Pattern)解释MVP架构。如果你想扩大你对MVC或MVP的研究,这是你正在寻找的。
Artem Zinnatullin 的M — Model in MVC, MVP, MVVC in Android
如果您还对模型图层中的JSON或SQL有一些误解,或者没有获取关于模型图层的精确认识,这篇文章引导您进一步了解模型层与其他层之间的区别。他的“模型层是解决方案”部分特别适合展示如何将代码从接口实现到测试的实例。
--