MVC的基本介绍
MVC全称是Model - View - Controller,是模型(model)-视图(view)-控制器(controller)的缩写。MVC是一种框架模式而非设计模式,GOF把MVC看作是3种设计模式:观察者模式、策略模式与组合模式的合体,而核心是观察者模式。简而言之,框架是大智慧,用来对软件设计进行分工;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
使用 MVC,把业务逻辑抽离到 Controller 中,让 View 层专注于显示 UI。
MVC的优点
(1)首先就是理解比较容易,技术含量不高,这对开发和维护来说成本较低也易于维护与修改。
(2)耦合性不高,表现层与业务层分离各司其职,对开发来说很有利。
MVC的缺点
(1)完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。
(2)对于小项目,MVC反而会带来更大的工作量以及复杂性。
MVC在Android中的应用
Android中对MVC的应用很经典,在Android中视图View层一般采用XML文件进行界面的描述。
而对于模型Model部分则大多对应于本地的数据文件或网络获取的数据体,很多情况下我们对这些数据的处理也会在这一层中进行。
最后的控制器Controller则当之无愧的是右Activity承担。
MVP介绍
在Android中,业务逻辑和数据存取是紧紧耦合的,很多缺乏经验的开发者很可能会将各种各样的业务逻辑塞进某个Activity、Fragment或者自定义View中,这样会使得这些组件的单个类型臃肿不堪。如果不将具体的业务逻辑抽离出来,当UI变化时,你就需要去原来的View中抽离具体业务逻辑,这必然会很麻烦并且易出错。
MVP中把Layout布局和Activity作为View层,增加了Presenter,Presenter层与Model层进行业务的交互,完成后再与View层交互(也就是Activity)进行回调来刷新UI。这样一来,所有业务逻辑的工作都交给了Presenter中进行,使得View层与Model层的耦合度降低,Activity中的工作也进行了简化。但是在实际项目中,随着逻辑的复杂度越来越大,Activity臃肿的缺点仍然体现出来了,因为Activity中还是充满了大量与View层无关的代码,比如各种事件的处理派发,就如MVC中的那样View层和Controller代码耦合在一起无法自拔。
MVP模式是MVC模式的一个演化版本,MVP全称Model-View-Presenter。目前MVP在Android应用开发中越来越重要了。
(1)MVP模式会解除View与Model的耦合,有效的降低View的复杂性。同时又带来了良好的可扩展性、可测试性,保证系统的整洁性和灵活性。
(2)MVP模式可以分离显示层与逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。
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 – 数据的存取:Model 角色主要是提供数据的存取功能。Presenter 需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白的说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据方式获取的集合。
MVP和MVC的对比
MVP架构:
View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
Presenter与View的交互是通过接口来进行的。
通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。
MVC架构:
View可以与Model直接交互。
Controller是基于行为的,并且可以被多个View共享。
可以负责决定显示哪个View。
通过一个简单的例子来理解MVP模式
View层:
/**
* ViewInterface
* 定义一些通用的view接口
*/
public interface LoadDataView {
/***
* 耗时操作,加载数据,显示Progress
*/
void showLoading();
/***
* 隐藏Progress
*/
void hideLoading();
}
/**
* 更细小的,用来显示图书细节的View接口
*/
public interface LoadBookView extends LoadDataView {
void showDetailsView(BookEntity entity);
}
/***
* Fragment,属于View层,实现了ViewInterface(LoadBookView)
*/
public class BookDetailFragment extends Fragment implements LoadBookView{
/**图书条形码ISBN号*/
public static final String ISBN = "9787121060748";
/**持Presenter对象*/
private BookDetailsPresenter presenter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initWidget();
presenter = new BookDetailsPresenter(getActivity(), ISBN); // 实例化一个presenter对象
presenter.setView(this); //让Presenter持一个ViewInterface实例(LoadBookView)
presenter.loadData(); //告诉Presenter快给我加载Data
}
@Override
public void showDetailsView(BookEntity entity) {
//更新UI等操作
}
@Override
public void showLoading() {
rlProgress.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
rlProgress.setVisibility(View.GONE);
}
}
2、Presenter层:
public class BookDetailsPresenter {
/**持一个Model层的对象,用来从网页接口Rest Api中提取数据*/
private RestApi restApi = null;
/**一个ViewInterface对象,用来回调Data给View*/
private LoadBookView loadBookView;
private String isbn;
public BookDetailsPresenter(Context context, String isbn) {
restApi = new RestApiImpl(context);
this.isbn =isbn;
}
public void setView(LoadBookView loadBookView) {
this.loadBookView = loadBookView;
}
public void loadData() {
loadBookView.showLoading();
//耗时操作,开个线程异步的加载数据
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
restApi.getBookDetailByIsbn(isbn, bookDetailsCallback);
}
});
thread.start();
}
//匿名内部类,接收bookDetailCallback的回调数据
private RestApi.BookDetailsCallback bookDetailsCallback = new RestApi.BookDetailsCallback() {
@Override
public void onBookEntityLoaded(BookEntity bookEntity) {
notifyDataLoadedSuccessful(bookEntity);
BookDetailsPresenter.this.loadBookView.hideLoading();
}
@Override
public void onError(Exception e) {
//异常后的相关处理
}
};
/***
* 通知获取数据成功了,赶快通知UI更新吧
*/
private void notifyDataLoadedSuccessful(final BookEntity bookEntity) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
BookDetailsPresenter.this.loadBookView.showDetailsView(bookEntity);
}
});
}
}
3、Model层:
/***
* 整个应用程序需要的数据实体类
*/
public class BookEntity {
//一些set,get方法
}
/**
* 一个接口,用来从rest api api获得数据,它的实现在RestApiImpl中
*/
public interface RestApi {
String API_ISBN_BASE_URL = "https://api.douban.com/v2/book/isbn/";
/***
* 更细小的接口,用来将获取到的数据,回调给它的调用者
*/
interface BookDetailsCallback {
void onBookEntityLoaded(BookEntity bookEntity);
void onError(Exception e);
}
/**
* 从网络获取数据,然后通过bookDetailCallback回调给Presenter
* @param isbn
* @param bookDetailsCallback
*/
void getBookDetailByIsbn(final String isbn, final BookDetailsCallback bookDetailsCallback);
}