小序
隨着開發框架的潮流影響,再加上組織上給予的壓力。看來不得不邁出MVC君的守望之門了,彷彿一股熱流向此處湧來,其實際上已然有好多前輩寫下了大寫的文章在等着我。此刻起只要好好了解一番,再弄一兩個小Demo便可造就一片新的天地了,然後就可以在藍天白雲下沐浴在陽光下奔跑了,光想想還是有點小激動的。
MVC
1.1.介紹
1.2.還是介紹
以上介紹主要是繪製並介紹了MVC層與層之間的交互過程,下面具體整理下各個層的含義與作用域:
who | what | where |
---|---|---|
Model | 業務邏輯處理層 | 數據源處理,網絡加載,複雜算法 |
View | 界面顯示層 | 佈局文件.xml |
Controller | 控制器 | Activity、Fragment |
1.3.潛在輸出
- 將內容顯示以及業務邏輯處理分隔開來,模塊職責劃分明確,有利於代碼維護;
- 提升項目的可擴展性與維護性,並降低耦合度;
- 對業務邏輯複雜且頁面較多的項目更能提升MVC的優勢;
1.4.承受傷害
- View與Controller融合在一起。大多數人通過Activity/Fragment直接控制View內容的更新;
- View完全依賴Model。忽略Controller的控制直接從Model獲取數據;
- View自己承擔部分業務邏輯。Model失去可重用的業務邏輯處理;
- Controller同時負責View與Model的任務處理。使得代碼異常臃腫,顯得較爲混亂;
1.5.代碼飄過
/**
8. Controller
*/
public class MainActivity extends AppCompatActivity {
// 文本
private TextView mMainTv;
// 按鈕
private Button mMainBtn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* View
*/
private void initView() {
this.mMainTv = (TextView) findViewById(R.id.main_tv);
this.mMainBtn = (Button) findViewById(R.id.main_btn);
mMainBtn.setOnClickListener(v -> {
mMainTv.setText(getDateTimeStr());
});
}
/**
* Model
*
* @return 當前日期時間
*/
private String getDateTimeStr() {
Date curDate = new Date(System.currentTimeMillis());
return SimpleDateFormat.getDateTimeInstance().format(curDate);
}
}
以上代碼只是簡單實現按鈕點擊獲取當前的時間,並賦值給文本顯示。其主要是表現大多數停留在MVC的夥伴,都會以這樣一個方式去創建模塊的頁面和功能。至少接觸過的很多項目裏是這樣的,當然並沒有反對的意思,因爲只要合理分配也是極好的。這裏更多的是爲了鑑於這個類來比較:
MVP
2.1.介紹
2.2.依然是介紹
同理,以上介紹主要是繪製並介紹了MVP層與層之間的交互過程。其情節內容沒有改動,因爲一個功能的思想一樣不受使用模式的影響。下面具體整理下各個層的含義與作用域:
who | what | where |
---|---|---|
Model | 數據處理層 | 數據的檢索,存儲等操作 |
View | 界面顯示層 | Activity、Fragment、佈局文件.xml |
Presenter | 層現器 | 作爲View與Model兩者的中間樞紐,處理與用戶交互的負責邏輯 |
2.3.潛在輸出
- View層與Model層完全分離,兩者間的修改互不影響;
- Presenter控制View與Model之間的交互,大大提升模型的使用效率;
- Presenter可以被多個View綁定(MVC當中Controller服務多個View);
- 可完全脫離用戶接口實現單元測試;
- 解決MVC當中Activity代碼臃腫的問題;
2.4.承受傷害
- 由於Presenter可以被多個View綁定,且如果與Presenter之間的交互現象過於頻繁,一旦View需要變更,那麼Presenter也需要變更從而影響Presenter的複用性;
- 大量的View與Model的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難;
2.5.前期裝備
啥?還有前期裝備,這是要逆天嘛,還有這裝備到底是什麼。請恕我直言,如果前期沒有輔助那必須喫力些呀,所以View有理由學會擺脫與Presenter的直接交互,繼而通過View interface來配合View接受Presenter返回結果的處理。下面是前期裝備屬性:
- 降低View與Presenter之間耦合度;
- 方便進行單元測試,可繞過View直接定義類實現interface模擬View與Presenter之間的交互;
2.6.後期裝備
啥?還有後期,這發育的也太狠了吧。請恕我直言,這裝備我也還沒用過。當然它的屬性是很誘人的,滿滿的被動輸出傷害有木有。其實際上就是前輩們挖掘出來的兩種View模式,用於劃分View與Presenter的任務內容,具體如下:
who | what |
---|---|
PV(Passive View) | View的UI元素委託給Presenter操作 |
SoC(Supervising Controller) | View自己負責UI處理以及數據綁定 |
2.7.代碼飄過
相信剛接觸MVP模式也許會像我一樣,摸不清構建順序吧。因爲項目比較緊急,且又急於嘗試MVP的實現,我僅在項目裏使用了MVP+XUtils3的框架。下面貼出的代碼僅供參考。
- 基礎構建
public class BasePresenter<V> {
public V mvpView;
public Context mContext;
public BasePresenter(Context context, V mvpView) {
this.mContext = context;
this.mvpView = mvpView;
}
public void detachView() {
this.mvpView = null;
}
}
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity {
public P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mvpPresenter = createPresenter();
}
protected abstract P createPresenter();
}
public abstract class BaseFragment<P extends BasePresenter> extends Fragement {
public P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mvpPresenter = createPresenter();
}
protected abstract P createPresenter();
}
- 具體實現,這裏以請求登錄操作爲例。先View interface後Presenter再Activity/Fragment(感覺順些)
public interface LoginView {
void userLoginSuccess(UserInfo userInfo);
void userLoginFail(String failMsg);
}
public class LoginPresenter extends BasePresenter<LoginView> {
public void loginUser(String account, String pwd) {
UserLogin userLogin = new UserLogin();
userLogin.Account = account;
userLogin.Password = pwd;
RequestParams params = new RequestParams(ApiConfig.USER_IP + ApiConfig.USER_LOGIN);
params.setBodyContent(userLogin.toJSONString());
// 請求結果需對mvpView判空處理
x.http().post(params, new Callback.CommonCallback<UserInfo>() {
@Override
public void onSuccess(UserInfo model) {
if (null == mvpView) return;
if (model.getStatusCode() == 200) {
mvpView.userLoginSuccess(model.getData());
} else {
mvpView.userLoginFail(model.getMessage());
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
if (null == mvpView) return;
mvpView.userLoginFail("請求出錯/網絡異常");
}
@Override
public void onCancelled(CancelledException cex) {}
@Override
public void onFinished() {}
});
}
}
@ContentView(R.layout.activity_login)
public class LoginActivity extends BaseActivity<LoginPresenter> implements LoginView {
@ViewInject(R.id.login_edt_id)
private EditText mIdEdt;
@ViewInject(R.id.login_edt_pwd)
private EditText mPwdEdt;
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter(this, this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);
}
@Event(value = {R.id.login_btn}, type = View.OnClickListener.class)
private void onAppClick(View view) {
String idStr = mIdEdt.getText().toString();
String pwdStr = mPwdEdt.getText().toString();
// 爲了代碼簡潔些,判空等處理被捨棄了
mvpPresenter.loginUser(idStr, pwdStr);
}
@Override
public void userLoginSuccess(UserInfo userInfo) {
// 登錄成功,執行相應操作
}
@Override
public void userLoginFail(String failMsg) {
// 登錄失敗,Toast提示錯誤信息
}
}
以上只是個人實踐內容,其實際上還有很多與MVP結合使用的大寫之作,推薦:
MVVM
3.1.介紹
3.2.當然還是介紹
以上繪製並介紹了MVVM層與層之間的交互過程,其左邊描述View與數據源的綁定,即控件顯示數據與綁定的對象數據同步更新(可以忽略時序圖的前後順序這一說,只怪提供的繪圖方法有限)。下面具體整理下各個層的含義與作用域:
who | what | where |
---|---|---|
Model | 數據處理層 | 數據的檢索,存儲等操作 |
View | 界面顯示層 | Activity、Fragment、佈局文件.xml |
ViewModel | 視圖模型 | 可理解爲View的Model和Presenter之間的融合 |
3.3.潛在輸出
- 數據捆綁UI更新。數據與業務邏輯處在一個獨立的ViewModel當中,由數據主導UI的更新(eg. DataBinding框架實現被捆綁的UI更新);
- 低耦合度。ViewModel不對View的任何控件保持持有狀態,更改UI無需修改ViewModel的實現;
- 團隊協作。扮演UI處理以及數據和業務邏輯的兩個不同角色;
- 複用性強。同一個ViewModel可以爲多個View服務;
- 單元測試。只需要在ViewModel執行單元測試,完全不需要依賴View的任何操作;
3.4.承受傷害
- 多個View與ViewModel執行捆綁,造成ViewModel比較笨重;
- 數據對象的直接引用使得後期維護起來會比較困難;
3.5.這裏木有代碼
只是瞭解了下但還沒有確切實踐過MVVM,暫時也就沒有代碼飄過了
總結
4.1.共同點
who | what |
---|---|
View | 一直扮演者與用戶交互的角色 |
Model | 因數據處理而忙碌着 |
4.2.不同點
who | what |
---|---|
Controller | 被動的負責將View的需求告知Model處理並讓Model通知View更新 |
Presenter | 在Model前面拿下View的請求做一定的業務處理纔給回Model做數據操作處理, 數據請求處理結束後再將Model數據返回給View並提示更新 |
ViewModel | 在Presenter的基礎上綁定並實現了對View的部分數據操作 |
PS1.沒有什麼絕對好的開發模式,倘若沒有規範好自身的代碼,一切都將是空談
PS2.以上內容若存在不足之處,還望大蝦們指點一二