通過代碼對比孕似,詳細講解MVC,MVP刮刑,MVVM之間應該如何選擇喉祭,以及對Android單元測試的探索。本文的側(cè)重點在于如何選擇雷绢,并沒有對每種架構模式概念展開詳解(網(wǎng)絡上這方面的文章有很多泛烙,大家可以自行搜索)。
大綱
- MVC or MVP or MVVM翘紊?
- 確定選型:MVP + DataBinding
- 單元測試(探索階段)
目的
- 提高開發(fā)效率
- 易于測試
- 擁抱變化
- 降低維護成本
Android中的MVC
示例:
展示任務詳情的功能蔽氨,詳情View層的通過xml來寫的,請求數(shù)據(jù)相關的代碼會在Model層提供接口霞溪,然后通過Activity對View和Model層進行連接孵滞。
代碼:
M:
public interface TaskModel {
void loadTask(String taskId, OnTaskListener listener);
}
V:
taskdetail.xml:界面布局文件,采用XML進行描述鸯匹,屬于V層的一部分坊饶。
C & V:
TaskDetailActivity:C層和V層
public class TaskDetailActivity {
private void initView() {
taskModel.loadTask(taskId, new OnTaskListener() {
public void onSuccess(Task task) {
//V層代碼,但是目前耦合到了C層
detailTitle.setText(task.getTitle);
}
});
}
}
總結:
- 編寫簡單快速殴蓬,適合含有簡單邏輯的業(yè)務匿级,或是demo程序。
- Activity的臃腫:xml作為view層染厅,控制能力太弱痘绎,無法動態(tài)的改變頁面的內(nèi)容,只能把代碼寫在activity中肖粮,造成了activity既是Controller層孤页,又是View層的問題。
- 耦合度較高涩馆,需求變化改動大行施,后續(xù)維護成本高允坚。
- Controller混雜著Android代碼無法Junit。
Android中的MVP
示例:
展示任務詳情的功能蛾号,詳情View層的通過xml和Activity來完成稠项,請求數(shù)據(jù)相關的代碼會在Model層提供接口,然后通過Presenter對View和Model層進行連接鲜结。
代碼:
M:
public interface TaskModel {
void loadTask(String taskId, OnTaskListener listener);
}
V:
taskdetail.xml:界面布局文件展运,采用XML進行描述,屬于V層的一部分精刷。
public class TaskDetailActivity implements TaskDetailView {
public void showTask(Task task) {
detailTitle.setText(task.getTitle);
}
}
P:
public class TaskDetailPresenter implements Presenter {
public void getTask() {
taskModel.loadTask(taskId, new OnTaskListener() {
public void onSuccess(Task task) {
//通過接口回調(diào)到V層更新UI
taskDetailView.showTask(task);
}
});
}
}
總結:
- 減少各層之間耦合拗胜,易于后續(xù)的需求變化,降低維護成本怒允。
- Presenter層獨立于Android代碼之外挤土,可以進行Junit測試。
- 接口和類較多误算,互相做回調(diào)仰美,代碼臃腫。
- Presenter層與View層是通過接口進行交互的儿礼,接口粒度不好控制咖杂。
Android中的MVVM
示例:
展示任務詳情的功能,詳情View層的通過xml和Activity來完成蚊夫,請求數(shù)據(jù)相關的代碼會在Model層提供接口诉字,然后通過ViewModel對View和Model層進行連接。
代碼:
M:
public interface TaskModel ...
void loadTask(String taskId, OnTaskListener listener);
V:
taskdetail.xml:界面布局文件知纷,采用XML進行描述壤圃,綁定規(guī)則在xml中進行定義。
TaskDetailActivity:Activity主要是初始化和補充的功能琅轧。
VM:
TaskDetailViewModel {
public void getTask() {
taskModel.loadTask(taskId, new OnTaskListener() {
public void onSuccess(Task task) {
//通過綁定技術更新UI伍绳,做到數(shù)據(jù)獨立于UI
taskDeatailViewBinding.setTask(task);
}
});
}
}
總結:
- 和MVP比較像,主要區(qū)別在于View和ViewModel / Presenter之間的通信
- 相比MVP優(yōu)勢是通過DataBinding技術為VM和V層進行數(shù)據(jù)綁定乍桂,提高開發(fā)效率冲杀,由于目前綁定技術的局限,V層一些界面的處理還是需要Activity的輔助睹酌。
- VM層摻雜Android代碼無法進行Junit測試权谁。
確定選型
通過以上對比,選擇了MVP+DataBinding憋沿,此架構模式基于MVP旺芽,并使用DataBinding庫來顯示數(shù)據(jù)并綁定View。它并不遵循嚴格的MVVM或MVP模式,因為它同時使用了ViewModel和Presenter采章。
DataBinding
這是我上篇文章我們?yōu)槭裁匆褂肈ataBinding字币,里面通過代碼的對比,總結說明為什么要使用DataBinding的技術共缕,有興趣的同學可以閱讀一下,在這里我把文章里的一小段總結貼出來:
DataBinding為數(shù)據(jù)驅(qū)動:數(shù)據(jù)變化后自動更新UI士复;事件處理:直接找到目標實例處理用戶操作的事件图谷。這樣我們就不需要和UI或者控件打交道,只需要在java代碼中處理業(yè)務邏輯就好了阱洪,非常清晰便贵,其余的統(tǒng)一交給binding庫去完成。降低了代碼耦合度冗荸,使得數(shù)據(jù)獨立于UI承璃,對以后程序的變化和維護都有積極的影響。
MVP+DataBinding
示例:
展示任務詳情的功能蚌本,數(shù)據(jù)和事件綁定與基礎MVP代碼的對比盔粹。
代碼:
//普通MVP 任務詳情代碼示例:
public void onCreateView(...) {
detailTitle = (TextView) root.findViewById(R.id.task_detail_title);
detailComplete = (CheckBox) root.findViewById(R.id.task_detail_complete);
detailComplete.setOnCheckedChangeListener(
(cb, isChecked) -> presenter.completeChanged(task, isChecked)
);
}
@Override
public void showDescription(String title) {
detailTitle.setText(title);
}
//xml文件省略...
//MVP+DataBinding 任務詳情代碼示例:
@Override
public void showTask(Task task) {
viewDataBinding.setTask(task);
}
//可以通過布局文件直接綁定到數(shù)據(jù)模型的屬性(xml文件)
<TextView
android:id="@+id/task_detail_title"
android:text="@{task.title}" />
//事件綁定同樣可以直接在布局文件中實現(xiàn)(xml文件)
<CheckBox
android:id="@+id/task_detail_complete"
android:checked="@{task.completed}"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
總結:
- DataBinding庫提高了開發(fā)效率,使得xml布局文件用于將數(shù)據(jù)綁定到UI元素程癌,也可以綁定一個action handler(Presenter)處理用戶操作的事件舷嗡,可以觀察和設置數(shù)據(jù),以便在需要時自動更新(雙向綁定)嵌莉。
- 需要對View和Presenter兩層做測試进萄,增加工作量。
- 目前Android Studio對Databing的支持不是太好(報錯和代碼自動生成)
單元測試(探索階段)
Presenter
不需要Android環(huán)境锐峭,因此使用Junit測試即可
- JUnit:Java語言的單元測試框架
- Mockito:模擬對象的測試框架
View
使用Google建議的Espresso進行UI的測試(需要依賴Android環(huán)境)
- AndroidJUnitRunner:Android 且與 JUnit 4 兼容的測試運行器
- Espresso:功能性 UI 測試框架
問題
就目前實踐過程中中鼠,會發(fā)現(xiàn)需要為測試寫一些額外的方法,不是太舒服沿癞。并且寫兩層測試援雇,工作量變大。還有覆蓋率以及維護的問題椎扬,一直在探索最佳實踐熊杨,會在以后的文章里面和大家分享。
本片文章來自于自己的編程實戰(zhàn)盗舰,寫的不好的地方請大家?guī)兔χ刚ЦM軒椭蠹疫x到合適自己的架構模式。
謝謝閱讀钻趋。