MVP能够有效地降低View的复杂性,避免业务逻辑被塞进View中,使得View变得很混乱。MVP模式会解除View与Model的耦合,同时又带来了良好的可扩展型,可测试性,保证了系统的整洁性,灵活性。
MVP结构图:
MVP模式可以分离显示层和逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI。
MVP并不是一个标准化的模式,它可以有很多种实现方式,我们也可以根据自己的需求和自己认为对的方式去修正MVP的实现方式,它可以随着Presenter的复杂程度变化。只要保证我们是通过Presenter将View和Model解耦合,降低类型复杂度,各个模块可以独立测试,独立变化,这就是正确的方向。
MVP模式的三个角色:
1. Presenter:交互中间人
Presenter主要作为沟通View和Model的桥梁,它从Model层检索数据后,返回给View层。使得View和Model之间没有耦合,也将业务逻辑从View角色上抽离出来
2. View:用户界面
View通常是指Activity,Fragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作通过会转交给Presenter进行实现,最后,Presenter调用View逻辑接口将结果返回给View元素。
3.Model:数据的存取
对于一个结构化的App来说,Model角色主要是提供数据的存取功能。Presenter需要通过Model层存储,获取获取数据,Model就像一个数据仓库。更直白地说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据获取方式的集合。我对于Model层的理解,一方面是数据的存取,另一方面不单单是表面意义上的存取,它可能只是实例化一个对象,用这个对象来存数据,也就是上面说的网路获取数据的角色。
实例:
从服务器下拉最新的几篇文章,显示在列表上
业务逻辑:
1.向服务器请求数据,并且将数据存储到数据库中
2.从数据库中加载文章列表
先从Presenter来看:哟用户需要从网络获取数据,因此需要一个数据获取的接口,也可以从本地数据库获取缓存的数据,因此需要一个从数据库加载缓存的接口。
package com.example.asus1.testmvp.Presenters;
import com.example.asus1.testmvp.Models.Articel;
import com.example.asus1.testmvp.Models.ArticelModel;
import com.example.asus1.testmvp.Models.ArticelModelImpl;
import com.example.asus1.testmvp.Views.ArticleViewInterface;
import java.util.List;
/**
* Created by asus1 on 2018/3/7.
*/
public class ArticlePresenter {
//ArticleView接口,代表了View接口角色
ArticleViewInterface mArticleView;
//文章数据的Model,也就是Model角色
ArticelModel mArticleModel = new ArticelModelImpl();
//从网络上获取文章的API
ArticleAPI mArticelAPI = new ArticleAPIImpl();
public ArticlePresenter(ArticleViewInterface viewInterface){
mArticleView = viewInterface;
}
//获取文章,也就是我们的业务逻辑
public void fetchArticels(){
mArticleView.showLoading();
mArticelAPI.fetchAeticel(new DataListener<List<Articel>>() {
@Override
public void onComplete(List<Articel> result) {
mArticleView.showArticel(result);
mArticleView.hideLoading();
//储存到数据库
mArticleModel.saveArticels(result);
}
});
}
public void loadArticelFromDB(){
mArticleModel.loadArticelsFromCache(new DataListener<List<Articel>>() {
@Override
public void onComplete(List<Articel> result) {
mArticleView.showArticel(result);
}
});
}
}
在ArticelPresenter中持有了View和Model的引用,还有一个操作网络请求的ArticelAPIImpl对象。ArticelViewInterface就是主界面的逻辑接口,代表了View接口角色,用于Presenter回调View的操作。
public interface ArticleViewInterface<T> {
void showLoading();
void hideLoading();
void showArticel(List<T> results);
}
ArticelModelImpl是对数据库的存取操作,用于保存网络上加载的数据,以及从数据库中加载文章缓存
public class ArticelModelImpl<T> implements ArticelModel<T> {
List<T> mCacheArticels = new LinkedList<>();
@Override
public void saveArticels(List<T> results) {
mCacheArticels.addAll(results);
}
@Override
public void loadArticelsFromCache(DataListener<List<T>> listener) {
listener.onComplete(mCacheArticels);
}
}
ArticelActivity需要实现ArticelViewInterface接口,并且需要建立与Presenter的联系,ArticelActivity的业务逻辑都将交给Presenter进行处理,处理结果通过ArticelViewInterface接口回调给ArticelActivity。
public class ArticelsActivity extends AppCompatActivity implements ArticleViewInterface<Articel>{
private ListView mListView;
private ProgressBar mProgressBar;
private List<Articel> mArticels = new LinkedList<>();
private ArrayAdapter mAdapter;
private ArticlePresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
//构建ArticelPresenter,与ArticelActivity建立关联
mPresenter = new ArticlePresenter(this);
//获取文章数据
mPresenter.fetchArticels();
}
private void initViews(){
mListView = (ListView)findViewById(R.id.articels_list_view);
mAdapter = new ArticelAdapter(this,R.layout.view_list_item,mArticels);
mListView.setAdapter(mAdapter);
//进度条
mProgressBar = (ProgressBar)findViewById(R.id.loading_progressbar);
}
@Override
public void showArticel(List<Articel> results) {
mArticels.clear();
mArticels.addAll(results);
mAdapter.notifyDataSetChanged();
}
......
通过这个例子,可以看到Presenter对于View是完全解耦的,presenter依赖的是ArticelViewInterface抽象,而不是ArticelActivity,当UI界面发生变化的时候,只需要新的UI实现了ArticelViewInterfaceUI及相关的逻辑即可与Presenter迅速地协作起来,成本非常低。ArticesActivity此时的作用只是做一些View的初始化工作,职责单一,功能简单,易于维护。
在MVP中,View和Model不能直接通信,它们的交互都是通过Presenter。上面代码中,Presenter中还持有一个ArticelModel对象,这个ArticelModel就是Model角色,它负责处理数据。ArticelNModel同样也是一个接口,因此Presenter与Model也依赖于抽象而不是具体,使得ArticelModel的具体实现可以被轻易地替换。
这个例子中,presenter没有进行接口抽象,而是使用了具体类,因为业务逻辑相对稳定,所以使用具体类即可。如果业务逻辑相对来说易于变化,使用Presenter接口来进行抽象是最好不过的。
由此可见,Model——View——Presenter三者之间的关系都是松耦合的,Presenter持有View,Model引用都是抽象的,这样当UI发生变化的时候,只需要替换View即可。当数据库需要替换的时候,值需要重新构建一个实现AtricelModel接口的类并且实现想管 的存取逻辑即可。这样使得View,Model,Presenter三者之间可以独立的变化,测试也非常方便,可扩展性,灵活性都很高。