Android 进阶之设计模式 二 MVP模式
MVP 引入的必要性
在上一篇博文结束的时候提到了MVC模式的缺点–View层和Module层会产生直接交互,这有何不可?我们知道一般View层都是一些Activity和Fragment,如果采用MVC模式就会导致整个Activity的臃肿。因为View层和Module层之间大部分时候是有业务逻辑的,拿回来的数据不一定就能直接展示,这就导致
Activity不但需要负责与用户之间的交互,又引入了大量的逻辑代码。为了解决这个问题,我们引入了MVP模式。
MVP的简介
MVP 实际上是从MVC模式演变过来的,它使用Presenter层将Module层和View层隔离开。它也是由三层结构组成:
下面是MVP模式下各层的划分规则:
View层:
View层通常是一些Activity、Fragment,以及各种View,它负责显示和处理和相应用户的交互事件,并且每个View中往往会包含一个或多个Presenter的引用。每个Presenter一般与一个Module相关,View层和Presenter层的交互是双向的,即View层可以调用Presenter的逻辑方法,Presenter也可以控制View层的信息展示。
Presenter层:
Presenter是Model层和View层的桥梁,在MVC模式中M,V是直接耦合的,而在MVP模式中通过Presenter将二者隔离开来,它负责从Model拿到数据进行处理并返回给View。Presenter和View层以及Modle层之间是通过接口进行交互的
Model层:
和MVC一样,只包含数据的模型以及数据模型的操作处理,它也可以包含两个部分dao和bean
总而言之:View层负责显示,Presenter负责逻辑处理,Model提供数据以及数据处理操作。下面是MVC和MVP之间结构的示例图:
二者的最大区别集中在控制层上,MVC的控制层相对分散,并且仅仅相当于一个转发其的作用,而在MVP模式下控制层的占的比重更大,也更加集中。使用MVP可以将Activty中包含的大量逻辑操作放到控制层中,从而避免Activity的臃肿。
下面是一个简单的例子:
读出和存入用户信息
首先实现Module层:
创建用于封装用户信息的bean对象:
public class UserBean {
private String mFirstName;
private String mLastName;
public UserBean(String firstName, String lastName) {
this. mFirstName = firstName;
this. mLastName = lastName;
}
public String getFirstName() {
return mFirstName;
}
public String getLastName() {
return mLastName;
}
}创建操作bean对象的dao 对象,这里用于将用户信息存储到数据库中,这里仅仅给出接口信息,具体实现不是该博文的重点。
public interface IUserDao {
public UserBean load(int id);
public void saveUser(int id, String firstName, String lastName);
}我们先看下上面的Module层,它的特点很明显,十分灵活,目前是将数据存储到数据库中,如果要将数据存储到远程服务器或者以其他形式存储,只要实现IUserDao,并根据需求覆写load 和 saveUser方法即可。
创建view层 这里仅仅列出接口,它的特点也是上述提到的十分灵活,要实现那种方式展示直接实现IUserView接口,并覆写这些方法即可。
public interface IUserView {
int getID();
String getFristName();
String getLastName();
void setFirstName(String firstName);
void setLastName(String lastName);
}创建presenter 通过IUserView和IUserDao接口操作model层和view层,Activity可以把所有逻辑转交给presenter处理,从而将逻辑从activity中分离出来,从而避免Activity的臃肿。
public class UserPresenter {
private IUserView mUserView;
private IUserDao mUserModel;
public UserPresenter(IUserView view) {
mUserView = view;
mUserModel = new UserDao();
}
public void saveUser( int id, String firstName, String lastName) {
mUserModel.saveUser(id, firstName, lastName);
}
public void loadUser( int id) {
UserBean user = mUserModel.load(id);
mUserView.setFirstName(user.getFirstName());
mUserView.setLastName(user.getLastName());
}
}Activity中实现IUserView接口,在其中操作view,实例化一个presenter变量,将Activity的逻辑转移给它。
public class MainActivity extends Activity implements OnClickListener,IUserView {
UserPresenter presenter;
EditText id,first,last;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout. activity_main);
findViewById(R.id. save).setOnClickListener( this);
findViewById(R.id. load).setOnClickListener( this);
id = (EditText) findViewById(R.id. id);
first = (EditText) findViewById(R.id. first);
last = (EditText) findViewById(R.id. last);
presenter = new UserPresenter( this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id. save:
presenter.saveUser(getID(), getFristName(), getLastName());
break;
case R.id. load:
presenter.loadUser(getID());
break;
default:
break;
}
}
@Override
public int getID() {
return new Integer( id.getText().toString());
}
@Override
public String getFristName() {
return first.getText().toString();
}
@Override
public String getLastName() {
return last.getText().toString();
}
@Override
public void setFirstName(String firstName) {
first.setText(firstName);
}
@Override
public void setLastName(String lastName) {
last.setText(lastName);
}
}有的时候比如Moduel存储结束后需要通知给Presenter,这个可以将presenter也抽出一个接口,并在调用Model层的Dao的时候将这个接口传入,在Module层操作完后,回调这个接口中的对应方法即可。
最后给大家泼点冷水,虽然MVP是很好的框架模式,但是在实际项目由于是多人开发维护的所以有时候并不是每个人都会遵守这个模式,特别是项目紧张的时候,所以很多时候会看到四不像的MVP。如果逻辑耦合性不是很强的话尚可重构,
如果耦合性大的话,就十分无奈了,并且一般经过测试的代码,大家都很“敬畏”,生怕动了一点引起了其他不容易发现的问题,所以很经常看到这种情况,也只能呵呵了。