Android開發(fā)中的MVP架構(gòu)
這篇文章背后的故事
最近退渗,越來越多的Android開發(fā)人員似乎在討論架構(gòu)冤寿。我周圍的同事和工程師也是如此雾叭。雖然我對MVP和DDD不太了解儒洛,但我們的新項(xiàng)目決定以MVP架構(gòu)為基礎(chǔ)。
這個(gè)帖子是我從各種職位和討論中研究和學(xué)習(xí)的册烈,其中包括:
為什么越來越多的人在談?wù)撨@個(gè)架構(gòu)强经?
什么是MVP,首先是什么掺涛?
MVC庭敦,MVVM或MVP?哪一個(gè)是最好的薪缆?
MVP的利與弊
上代碼螺捐!
還有,這是這篇文章不包括的內(nèi)容:
- 詳細(xì)的可運(yùn)行的樣例代碼
- 如何編寫測試代碼
文章的最后矮燎,我會(huì)告訴你進(jìn)一步閱讀這些主題
順便說一下定血,我上周在一個(gè)關(guān)于MVP架構(gòu)的地方研討會(huì)上發(fā)表了講話。這個(gè)帖子也是為了與演講對應(yīng)诞外。
介紹?Activity是上帝?
首先澜沟,讓我們來談?wù)凙ndroid開發(fā)提出簡潔架構(gòu)的必要性的根本原因。
以下是“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很難維護(hù)峡谊,了解發(fā)生了什么茫虽,執(zhí)行單元測試,擴(kuò)展階段等等既们。這是避免創(chuàng)造god classes的黃金規(guī)則濒析。
然而,在Android開發(fā)中啥纸,如果您不太在意架構(gòu)号杏,那么Activity類往往會(huì)越來越大.這是因?yàn)樵贏ndroid中,View和其他線程可以在Activity類中共存斯棒。最大的問題盾致?業(yè)務(wù)邏輯和UI邏輯在Activity類中共存主经。 這導(dǎo)致了單元測試或維護(hù)性的困難。
這是需要簡潔架構(gòu)的原因之一庭惜。不僅活動(dòng)類的擴(kuò)展罩驻,還有其他問題,如Activity和Fragment類中的復(fù)雜生命周期护赊,數(shù)據(jù)綁定等惠遏。
什么是MVP?
MVP代表Model, View, and Presenter.
View 是一個(gè)處理所有用戶操作并顯示每個(gè)視圖部分的圖層骏啰。在Android上节吮,這可以是Activity和Fragment類。
Model是負(fù)責(zé)數(shù)據(jù)訪問的層器一。數(shù)據(jù)是從哪里來的课锌。例如遠(yuǎn)程服務(wù)器API厨内,本地?cái)?shù)據(jù)庫祈秕,如SQL,SharedPreferences等雏胃。
Presenter是在View和Model模式之間橋接(或適配器)數(shù)據(jù)的層请毛。
關(guān)鍵是,較高的接口不了解較低的接口瞭亮,或者更準(zhǔn)確地說方仿,較高的接口不能,不應(yīng)該也不能知道較低的接口的細(xì)節(jié)统翩。是的仙蚜,信息隱藏。
依賴規(guī)則厂汗?
Uncle Bob的 “The Clean Architecture” 對于依賴規(guī)則是非常有用的委粉。
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
- 可以是一個(gè)帶有方法的對象
- 可以是一組數(shù)據(jù)結(jié)構(gòu)和功能
- 只要這些實(shí)體可以被企業(yè)中的許多不同的應(yīng)用程序使用就是沒問題的。
Use Cases 用例
- 包含應(yīng)用程序的業(yè)務(wù)規(guī)則
- 協(xié)調(diào)數(shù)據(jù)流往實(shí)體的流程
- 指導(dǎo)這些實(shí)體使用企業(yè)范圍的業(yè)務(wù)規(guī)則來實(shí)現(xiàn)用例的目標(biāo)
Presenters, Controllers
- 從用例和實(shí)體最方便的格式轉(zhuǎn)換數(shù)據(jù)娶桦,
- 轉(zhuǎn)換到一些外部代理(如DB或Web)最方便的格式
- 完全包含一個(gè)GUI的MVC架構(gòu)
外部接口贾节,UI,DB
- 所有的細(xì)節(jié)都在哪里
- 如DB衷畦,Web框架等栗涂。
MVC,MVP還是MVVM祈争?
那么哪一個(gè)是最好的斤程?哪一個(gè)優(yōu)于其他人?我應(yīng)該選擇其他中的唯一一個(gè)的嗎菩混?
答案是 不暖释。
這些模式的動(dòng)機(jī)是一樣的袭厂。如何避免復(fù)雜的結(jié)構(gòu)混亂的代碼,讓您輕松執(zhí)行單元測試球匕,并創(chuàng)建更高品質(zhì)的應(yīng)用程序纹磺。而已。
當(dāng)然亮曹,除了三種以外橄杨,還會(huì)有更多的模式。他們中的每一個(gè)都不是殺手锏照卦,也不是唯一的答案式矫。它們是方法論之一。解決問題的一個(gè)方法役耕。不要把手段變成目的采转。
利弊
好的,讓我們回到MVP架構(gòu)瞬痘。剛才故慈,我們已經(jīng)看到了MVP是什么,現(xiàn)在我們趁熱打鐵來討論MVP或其他體系結(jié)構(gòu)框全,以及MVC察绷,MVP和MVVM之間的區(qū)別。
優(yōu)點(diǎn)
- 可測試(導(dǎo)致TDD)
- 可維護(hù)(代碼重用)
- 容易得到審查
- 信息隱藏
缺點(diǎn)
- 冗長津辩,特別是當(dāng)應(yīng)用程序大小小的時(shí)候
- 額外的學(xué)習(xí)曲線(也許)
- 在開始編碼之前需要時(shí)間(但我敢打賭拆撼,架構(gòu)是所有開發(fā)必須的步驟)
口說無憑,代碼為證
這里只顯示MVP模式的最小結(jié)構(gòu)喘沿。如果您想看到更多的例子或活潑的例子闸度,請參考最后的“鏈接和資源”一章。這里只顯示MVP模式的最小結(jié)構(gòu)蚜印。如果您想看到更多的例子或活潑的例子莺禁,請參考最后的“鏈接和資源”一章。有更多豐富和精心設(shè)計(jì)的示例晒哄,基本上托管在Github中睁宰,以便您可以克隆并查看其在設(shè)備上的工作原理。
首先寝凌,我們定義每個(gè)視圖的接口柒傻。
/**
* 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類。這里的關(guān)鍵是:
- TopActivity僅處理事件偵聽器或顯示每個(gè)視圖部分
- 必須將所有業(yè)務(wù)邏輯委派給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也是如此萎馅。您不必關(guān)心數(shù)據(jù)結(jié)構(gòu)是什么双戳,以及業(yè)務(wù)邏輯如何工作。因此糜芳,您可以執(zhí)行TopUseCase的單元測試飒货,因?yàn)闃I(yè)務(wù)邏輯與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();
}
}
是的峭竣,您當(dāng)然可以執(zhí)行單元測試塘辅,即使業(yè)務(wù)邏輯在Activity類中實(shí)現(xiàn),但這需要更多的時(shí)間和復(fù)雜性皆撩。運(yùn)行應(yīng)用程序可能需要更多時(shí)間扣墩。相反,您應(yīng)該充分利用單元測試庫的力量扛吞,如Robolectric呻惕。
結(jié)論
沒有殺手锏,而MVP本身就是其中之一喻粹。它可以與其他方法混合使用蟆融,同時(shí)也可以選擇性地用于每個(gè)項(xiàng)目草巡。
鏈接和資源
Uncle Bob 的 The Clean Architecture
這是 Uncle Bob的帖子守呜,描述依賴規(guī)則是什么,以及每個(gè)組件之間的工作原理山憨。我開始說的圖形是受他的帖子的啟發(fā)查乒。雖然這并不是一直以Android開發(fā)為重點(diǎn),但他的話語意味深長并且組織的也不錯(cuò)郁竟。必須讀一個(gè)玛迄。
Fernando Cejas 的Architecting Android…The clean way?
我認(rèn)為這是最著名和最受歡迎的博客文章,解釋如何將MVP架構(gòu)納入Android開發(fā)棚亩。我也從他易于閱讀和寫得很好的博客文章中看到了“MVP”這個(gè)詞蓖议。每個(gè)想要了解MVP架構(gòu)如何在真實(shí)應(yīng)用程序中工作的Android開發(fā)人員,他的Github示例代碼應(yīng)該被克隆讥蟆。
Thanos Karpouzis 的 Android Architecture
MVC勒虾,MVP和MVVM在Android項(xiàng)目上的簡單指南。我從他平凡但廣泛覆蓋的帖子中學(xué)到了很多瘸彤,特別是MVC修然,MVP和MVVM之間的差異。
Software Design patterns on Android English
這是Karumi的高級Android開發(fā)人員的一個(gè)演講,它從一些設(shè)計(jì)模式(例如Renderer Pattern愕宋,Repository Pattern and Command Pattern)解釋MVP架構(gòu)玻靡。如果你想擴(kuò)大你對MVC或MVP的研究,這是你正在尋找的中贝。
Artem Zinnatullin 的M?—?Model in MVC, MVP, MVVC in Android
如果您還對模型圖層中的JSON或SQL有一些誤解囤捻,或者沒有獲取關(guān)于模型圖層的精確認(rèn)識(shí),這篇文章引導(dǎo)您進(jìn)一步了解模型層與其他層之間的區(qū)別邻寿。他的“模型層是解決方案”部分特別適合展示如何將代碼從接口實(shí)現(xiàn)到測試的實(shí)例最蕾。
--