您的位置:首页 > 其它

[置顶] 设计模式:从mvc到mvp

2017-01-14 16:48 369 查看

mvc与mvp概述

MVP是Model(数据) View(界面) Presenter(表现层)的缩写,它是MVC架构的变种,强调Model和View的最大化解耦和单一职责原则

Model:负责数据的来源和封装,比如网络请求类,数据库操作类以及java bean,如果有必要则提供接口暴露自己处理数据的状态和进度。

View:负责UI相关,如布局UI的初始化,各种listener的设置。在Android中,我们通常写的Activity和Fragment就是属于View层;在web开发中,html则是View层。

Controller:业务逻辑控制器,主要负责当获取到数据后对数据进行逻辑处理,然后将数据绑定到View上;比如:请求一个url,从网络获取到数据,进行解析javabean,然后各种set数据。对于控制器的概念大家很好理解,因为我们每天都在这样做,在Activity中请求数据然后更新UI。但是结合View的概念来看,很显然Activity和Fragment不但承担了View的任务,还负责完成的Controller的功能,随着业务功能的增多,Activity的代码越来越难以阅读和维护,这就是在Android中使用MVC的弊端,为了解决MVC模式下View层的臃肿,MVP模式应运而生。

Presenter:专门从C独立出来的业务逻辑层,主要负责处理原先View层的业务逻辑,解决了Activity的臃肿问题,让Activity只负责处理UI,职责更加明确;并且将View层的业务逻辑抽取到P层之后,View层与Model层也实现了解耦;便于后期代码的扩展和维护,并且业务逻辑层独立后代码还得到很大的重用性。

下面我用具体的代码的实例来分析,我以项目中的登录模块作为例子:

这里我啰嗦俩句吧:初学者可能会错误的认为MVP模式就是把数据层的代码写在modle包下面,界面写在view,业务逻辑写在presenter包中,MVP架构也就仅此而已,在此我需要特别强调一点的就是:

即使你进行的 分包,但是你的类之间互相持有对方的引用(就是在类内部互相new),互相依赖,那么分不分包是没有丝毫意义的,

就跟java语言中的封装一样,如果你写了个phone类,却又担负起照相、看电影等责任,那么谈抽象,谈封装就没什么意义。或者,写了一个照相的方法,还要把修照片的等多个功能也加在这个方法里,那么这个方法也就仅仅这当前用而已。

对于一个程序员来说,最重要的不是看代码逻辑,而要把更多的注意力放在代码优化,很多程序员不知道自己努力的方向吗,而说起来很简单,就是下面几句话:

如何让自己的代码 低藕合,高复用,易测试,好维护

mvc回顾

好了,继续MVP模式的讲解,为了更好的理解MVP,我先通过具体代码回顾下mvc,然后对比mvp,先用一个mvc设计模式写一个登录逻辑代码。

public class LoginActivity extends AppCompatActivity {

@InjectView(R.id.username)
EditText etUsername;
@InjectView(R.id.pwd)
EditText etPwd;
@InjectView(R.id.activity_login)
LinearLayout activityLogin;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
}

// 登录按钮的点击事件
@OnClick(R.id.activity_login)

fffb
public void onClick() {
// 1、获取输入框的数据
String username = etUsername.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();

// 2、 检查用户输入的合法性
if(checkInput(username, pwd)){
// 3、 执行登录操作
String url = "www.baidu.com";
HttpUtil.getInstance().post(url, new   HttpUtil.HttpUtilCallBack() {
@Override
public void onSuccess(String response) {
// 4、登录成功,保存用户数据
saveUserInfo();
// 5、跳转界面
jumpActivity();
}

@Override
public void onFail(String errorInfo) {
// 登录失败
}
});
}

}

private boolean checkInput(String username, String pwd) {
if(TextUtils.isEmpty(username) && TextUtils.isEmpty(pwd)){
showEmptyInfo();
return false;
}
if(username.length() != 11){
showLenthErrorInfo();
return false;
}
return true;
}

private void showEmptyInfo() {
Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
}

private void showLenthErrorInfo() {
Toast.makeText(this, "用户名长度不正确", Toast.LENGTH_SHORT).show();
}

private void saveUserInfo() {
}

private void jumpActivity() {
}

}


下面是我虚拟的一个网络请求,就是我们Model层的代码

public class HttpUtil {
private HttpUtil() {}
private static HttpUtil httpUtil = new HttpUtil();
public static HttpUtil getInstance(){
return httpUtil;
}

public void post(String url, HttpUtilCallBack callBack){
......
}

public interface HttpUtilCallBack{
void onSuccess(String response);
void onFail(String errorInfo);
}
}


从上面的代码中可以看到,MVC模式下弊端,很显然Activity不但承担了View的任务,还负责完成的Controller的功能,随着业务功能的增多,Activity的代码越来越难以阅读和维护,这就是在Android中使用MVC的弊端。

从mvc中抽取出p层

下面从上面的Activity中抽取出一个P 层,继续来看代码,我们不去动原来的Mode层。 很简单的抽取出一个p(LoginPresenter)层,把业务逻辑放到p层中,原来的LoginActivity仅仅作为一个View

下面是抽取后的LoginActivity的代码:

public class LoginActivity extends AppCompatActivity{

@butterknife.InjectView(R.id.username)
EditText etUsername;
@butterknife.InjectView(R.id.pwd)
EditText etPwd;
@butterknife.InjectView(R.id.activity_login)
LinearLayout activityLogin;

private LoginPresenterImpl mLoginPresenterImpl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
butterknife.ButterKnife.inject(this);

mLoginPresenterImpl = new LoginPresenterImpl(this);
}

@butterknife.OnClick(R.id.activity_login)
public void onClick() {
String username = etUsername.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();

mLoginPresenterImpl.login(username, pwd);
}

public void showEmptyInfo() {
Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
}

public void showLenthErrorInfo() {
Toast.makeText(this, "用户名长度不正确", Toast.LENGTH_SHORT).show();
}
}


先不看LoginPresneter的代码,也先不去看MVC中LoginActivity中的代码,就看MVP中的Activity,仅仅作为了一个View界面的展示,明白了这一点,可以去对比MVC中的LoginActivity了,看看那些地方改动。对比之后,下面贴出抽取出的P层代码:

public class LoginPresenterImpl  {
private LoginActivity mLoginActivity;

public LoginPresenterImpl(LoginActivity loginActivity) {
mLoginActivity = loginActivity;
}

public void login(String username, String pwd) {
// 2、 检查用户输入的合法性
if(checkInput(username, pwd)){
// 3、 执行登录操作
String url = "www.baidu.com";
HttpUtil.getInstance().post(url, new HttpUtil.HttpUtilCallBack() {

@Override
public void onSuccess(String response){
// 保存用户信息
saveUserInfo();
// 界面跳转
jumpActivity();
}

@Override
public void onFail(String errorInfo) {
}
});
}
}

private boolean checkInput(String username, String pwd) {
if(TextUtils.isEmpty(username) && TextUtils.isEmpty(pwd)){
mLoginActivity.showEmptyInfo();
return false;
}
if(username.length() != 11){
mLoginActivity.showLenthErrorInfo();
return false;
}
return true;
}

private void saveUserInfo() {
}

private void jumpActivity() {
}
}


看以看到业务逻辑的代码在LoginPresneter中。

但是,还是有很大的不足,耦合性太强,我们仔细分析代码就可以看到,在LoginActivity中有创建了一个LoginPresenter实例,而在LoginPresenter中创建了一个LoginActivity实例对象,V层和P之间互相依赖,谈何 低藕合,高复用,易测试的模块化设计。

p层和v层实现解耦操作

下面我们对P层和V层进行一个解耦操作,首先,创建一个LoginActivity的接口类,这个类里面,主要是一些P层业务逻辑代码执行结果的回调。



我们看到在P层和V层明显多了一个借口,

先来看看我们的LoginPresenter接口里面的代码

public interface LoginPresenter {
void login(String username, String pwd);
}


没错,就是把原来的Presnter类里面的业务方法进行了统一的规制声明

而LoginView借口里面则是业务逻辑执行结果的一些回调。

public interface LoginView {
void onLoginState(boolean success, String msg);
}


下面是新的LoginPresenterimpl的代码

public class LoginPresenterImpl  implements LoginPresenter{
private LoginView mLoginView;

public LoginPresenterImpl(LoginView loginView) {
mLoginView = loginView;
}

public void login(String username, String pwd) {
// 2、 检查用户输入的合法性
if(checkInput(username, pwd)){
// 3、 执行登录操作
String url = "www.baidu.com";
HttpUtil.getInstance().post(url, new HttpUtil.HttpUtilCallBack() {

@Override
public void onSuccess(String response){
mLoginView.onLoginState(true, response); // 业务逻辑执行成功的回调
// 保存用户信息
saveUserInfo();
// 界面跳转
jumpActivity();
}

@Override
public void onFail(String errorInfo) {
mLoginView.onLoginState(false, errorInfo); // 业务逻辑执行失败的回调
}
});
}
}

private boolean checkInput(String username, String pwd) {
if(TextUtils.isEmpty(username) && TextUtils.isEmpty(pwd)){
return false;
}
if(username.length() != 11){
return false;
}
return true;
}

private void saveUserInfo() {
}

private void jumpActivity() {
}
}


可以很清楚的看到,现在LoginPresenterImpl类里面不再有LoginActivity的引用,而仅仅是一个LoginView的接口,LoginPresenterImpl就不仅仅属于LoginActivity,而属于所有实现了LoginView接口的类。实现了LoginPresenter的解耦,下面再看LoginActivity里面的代码呢

public class LoginActivity extends AppCompatActivity implements LoginView{

@butterknife.InjectView(R.id.username)
EditText etUsername;
@butterknife.InjectView(R.id.pwd)
EditText etPwd;
@butterknife.InjectView(R.id.activity_login)
LinearLayout activityLogin;

private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
butterknife.ButterKnife.inject(this);

mLoginPresenter = new LoginPresenterImpl(this);
}

@butterknife.OnClick(R.id.activity_login)
public void onClick() {
String username = etUsername.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();

mLoginPresenter.login(username, pwd);
}

public void showEmptyInfo() {
Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show();
}

public void showLenthErrorInfo() {
Toast.makeText(this, "用户名长度不正确", Toast.LENGTH_SHORT).show();
}

@Override
public void onLoginState(boolean success, String msg) {
if(success){
// 登录成功
} else {
// 登录失败
}
}
}


对,大家看到了,在LoginActivity还是创建了Loginpresneterimpl的实例对象,还是没有实现彻底的解耦。而我要说的mvp模式,到这里也基本结束,这个具体解释看完下面的总结,大家应该能理解。

总结

对MVP模式,每个人都有自己的理解,谷歌官方也给出了MVP代码,谷歌对MVP的理解把Acitivity看成一个数据、界面。控制器之外的另一个控制器,仅仅控制自己生命周期,仅此而已,而界面View展示则交给fragment去展示。但是没有太多人去跟随谷歌,

大多数人还是把Activity看成是一个View,包括我也一样。mvp有优点,也有缺点,优缺点在我上面代码从mvc到mvp一个过程也一目了然,一路下来,功能还是那个功能,当时类却暴增,仅仅一个登陆模块,就需要这么类,对于一些中小型项目来说,使用mvp未免让项目更加复杂了,要是再去为继续解耦,就得继续往上抽取,在上层进行一个对象的实例化,像谷歌那样,还要为每一个Activity创建一个Fragment,那创建的类还是要更多,所以,上面解耦操作到此为适中。

当然,以上只是个人对mvp的理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: