作者: 夏至 歡迎轉(zhuǎn)載,也請(qǐng)保留這份申明,謝謝。
1、什么是MVP
MVP,全稱(chēng) Model-View-Presenter。它是從 MVC中演變過(guò)來(lái)的,它的基本思想是相通的;在MVP中,View更加專(zhuān)注于處理數(shù)據(jù)的可視化以及用戶交互,讓Model專(zhuān)注于數(shù)據(jù)的處理,而Presenter則,提供 View 與 Model 之間數(shù)據(jù)的紐帶,用于交互與數(shù)據(jù)傳輸;如下面這張圖:
可以看到,在View 與 Model 之間我們是通過(guò) Presenter,也就是 interface 來(lái)實(shí)現(xiàn)view 與數(shù)據(jù)的交互的,大大降低耦合,方便進(jìn)行單元測(cè)試。至于與 MVC 的異同,自行g(shù)oogle吧,這里就不細(xì)說(shuō)了。
其實(shí),自己在寫(xiě)代碼的時(shí)候,心中有個(gè)概念就好了,view 就是UI,model就是數(shù)據(jù)處理,而persenter 則是他們的紐帶。心中有個(gè)輪廓,寫(xiě)起來(lái)就不那么費(fèi)勁了。
2、使用 MVP 的 優(yōu)缺點(diǎn)
我們?cè)谑褂靡环N設(shè)計(jì)模式的時(shí)候,首先都會(huì)問(wèn),為什么要用這種模式,能給我們帶來(lái)哪些方便?用了這種模式,它的缺點(diǎn)會(huì)不會(huì)給我的工程造成影響?
首先,優(yōu)點(diǎn)上,我們上面已經(jīng)闡述了;
- 減低耦合,實(shí)現(xiàn)了 Model 與View 的真正分離,修改 View 而不影響 Model
- 模塊職責(zé)分明,層次分明,便于維護(hù),多人開(kāi)發(fā)首選。
- Presenter 可以服用,一個(gè) Presenter可以用于多個(gè) View,不用去改 Presenter
- 利于單元測(cè)試。模塊分明,那么我們編寫(xiě)單元測(cè)試就變得很方便了,而不用特別是特別搭平臺(tái),人工模擬用戶操作等等耗時(shí)耗力的事情。
缺點(diǎn):
對(duì)于小工程,額外多出來(lái)的代碼量,和額外的代碼復(fù)雜度,畢竟那么多 interface ,但對(duì)于它的有點(diǎn)來(lái)說(shuō),完全可以接受。
3、實(shí)戰(zhàn)
我們就簡(jiǎn)單一個(gè)數(shù)據(jù)保存的例子好了。至于數(shù)據(jù)庫(kù)的保存,采用郭霖大神的 LitePal,連接如下:
http://blog.csdn.net/column/details/android-database-pro.html
先上效果:
非常簡(jiǎn)單,就是獲取 EditText的數(shù)據(jù),保存在數(shù)據(jù)庫(kù),然后重新把它讀取出來(lái),結(jié)構(gòu)圖如下:
首先,從上面的效果圖來(lái)看,我們需要 name 和 password 這兩個(gè)字符串,我們需要新建一個(gè) User 類(lèi),由于要用到 LitePal ,所以讓它繼承 DataSupport;如果你使用自己寫(xiě)的,那就不用繼承啥了。:
public class User extends DataSupport{
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
Model:
然后,我們思考一下,從View 傳過(guò)來(lái)的數(shù)據(jù)name和password,我們是要保存起來(lái)的,所以,我們先在 model,編寫(xiě)數(shù)據(jù)保存和讀取的方法:
public interface IUserModel {
void saveUserData(User user);
User readUserData(String name);
}
它的具體實(shí)現(xiàn)方法如下:
/**
* Created by zhengshaorui on 2017/4/9.
*/
public class UserModel implements IUserModel {
public UserModel() {
}
/**
* 使用Litepal保存數(shù)據(jù)
* @return
*/
@Override
public void saveUserData(User user) {
user.save();
lg.d("user: "+user);
}
/**
* 通過(guò) name從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)
* @param name
* @return
*/
@Override
public User readUserData(String name) {
List<User> userList = DataSupport.where("username = ?",name).find(User.class);
if (userList.size() > 0)
return userList.get(0);
return null;
}
}
方法很簡(jiǎn)單,就保存一下數(shù)據(jù)和讀數(shù)據(jù)。什么?數(shù)據(jù)庫(kù)保存這樣就行了?是的,所以趕緊去學(xué)習(xí) LitePal 吧。
View:
View這里,我們是專(zhuān)注于UI的顯示和用戶交互的,上面我們通過(guò)名字的方式從數(shù)據(jù)庫(kù)中讀取,然后把它顯示出來(lái)。所以,我們需要添加 name 和password 的顯示方法,當(dāng)然還有出錯(cuò)的方法:
public interface IUserView {
void setUserName(String userName);
void setPassword(String password);
void error(String errormsg);
}
然后讓mainactivity 繼承這個(gè)接口重寫(xiě)該方法:
@Override
public void setUserName(String userName) {
mUserEditText.setText(userName);
}
@Override
public void setPassword(String password) {
mPassEditText.setText(password);
}
@Override
public void error(String errormsg) {
Toast.makeText(this, errormsg, Toast.LENGTH_SHORT).show();
}
Presenter :
好了,現(xiàn)在我們的 View 和 Model 都是單獨(dú)開(kāi)了的,所以,我們需要一個(gè)紐帶,把view 和model 連接起來(lái);那就是我們的 Presenter 了:
/**
* Created by zhengshaorui on 2017/4/9.
*/
public class UserPresenter {
private IUserView mIUserView;
private IUserModel mIUserModel;
public UserPresenter(IUserView mIUserView) {
this.mIUserView = mIUserView;
mIUserModel = new UserModel();
}
/**
* 數(shù)據(jù)保存
* @param user
*/
public void saveUser(User user){
mIUserModel.saveUserData(user);
}
/**
* 讀取數(shù)據(jù)
* @param name
*/
public void readUser(String name){
User user = mIUserModel.readUserData(name);
lg.d("getread: "+user);
if (user != null) {
mIUserView.setUserName(user.getUsername());
mIUserView.setPassword(user.getPassword());
}else{
mIUserView.error("沒(méi)有找到");
}
}
}
ok,其他都寫(xiě)好了,那么接下來(lái),只要寫(xiě)onclick事件就可以了:
//保存
public void save(View view){
User user = new User();
user.setUsername(mUserEditText.getText().toString());
user.setPassword(mPassEditText.getText().toString());
mUserPresenter.saveUser(user);
}
//清空 edittext
public void clear(View view){
mUserEditText.setText("");
mPassEditText.setText("");
}
//讀數(shù)據(jù)
public void read(View view){
mUserPresenter.readUser(mReadEditText.getText().toString());
}
這樣,我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的 MVP demo了。
疑問(wèn):
- 這樣,你可能會(huì)有疑問(wèn)?一個(gè)簡(jiǎn)單的demo被你寫(xiě)得這么復(fù)雜,還說(shuō)這個(gè)設(shè)計(jì)模式有多好?
- 這么簡(jiǎn)單的,接口都那么多,那稍微復(fù)雜點(diǎn)的,那接口不是直接上天和太陽(yáng)肩并肩了嗎?
可能很多人有這樣的疑問(wèn),是的,在一些小工程,或者一個(gè)項(xiàng)目只有一兩個(gè)人開(kāi)發(fā)的時(shí)候,你可以酌情考慮要不要使用這種模式,而使用這種模式,就考驗(yàn)?zāi)銓?duì)接口的理解和使用了。
而稍微復(fù)雜點(diǎn)的,和多人開(kāi)發(fā)的,我建議是使用這種模式的,當(dāng)然沒(méi)有完美的框架,適合自己的才是最重要的,反正我用的挺嗨的,有點(diǎn)搞逼格的感覺(jué),筆者就試過(guò)一個(gè)稍微復(fù)雜的項(xiàng)目,多人開(kāi)發(fā),最后搞得很混亂,牽一發(fā)而動(dòng)全一身;
當(dāng)然也是見(jiàn)仁見(jiàn)智,最后附上demo的下載地址:https://github.com/LillteZheng/MvpDemo