欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MVC与MVP对比

程序员文章站 2022-07-12 22:31:28
...

MVC模式

MVC的全称是Model-View-Controller,模型-视图-控制器。MVC是一种框架模式而非设计模式。GOF把MVC看作是三种设计模式——观察者模式、策略模式与组合模式的合体,核心是观察者模式,也就是一个基于发布/订阅者模型的框架。

MVC与MVP对比

在Android开发中,View对应的是各个布局文件,Controller对应的是Activity,而Model层主要是对应于从本地或网络获取数据、处理数据等(更符合实际的说法应该是,View与Controller层都是Activity)。
用户与视图交互,视图接收并反馈用户的动作;视图把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图格式化和渲染返回的数据,对于返回的数据完全可以增加用户体验效果展现给用户。
一个模型可以有多个视图,一个视图可以有多个控制器,一个控制器也可以有多个模型。

MVC的分层好处在于:
* 可以在不依赖于业务逻辑的情况下专注于视图设计
* 让程序的测试更容易
* 简化了分组开发。不同的开发人员可以同时开发视图、控制器逻辑和业务逻辑。

可以看到,MVC的主要目的是为了实现视图和数据分离(个人感觉在Android中实际上并没有实现)。

关于MVC在Android中的对应关系,网上的资料比较混乱,因为作为V层的布局文件,能做的事情却非常有限;而作为C层的Activity,却又具有操作UI的功能,也做了许多V层的事情。个人比较认同的是Activity同时充当V层与C层,因为Activity还负责了展示布局、增添View等操作,这就使得Activity既当爹又当妈。这显然不符合软件设计原则中的“单一职责”原则,当业务比较复杂的时候,会导致Activity的过于臃肿,动辄几千行代码。


MVP模式

MVC中Controller对应的是Activity,但Activity不仅做了C层该做的事情比如派发各类事件,还做了V层的一些UI操作事情,Activity作为C层是非常重的,各层次之间的耦合情况也是比较严重,不方便做单元测试,同时当业务复杂时Activity中的代码量太大,开发维护太难。
这个时候MVC的进化版MVP就诞生了。MVP全称是Model-View-Presenter。在MVP中,
* M层依旧负责数据的获取、处理
* Activity/Fragment充当View层,负责创建/更新视图
* P层则专门负则具体的业务逻辑

MVC与MVP对比

用户与V层交互,V层根据用户的操作调用P层的逻辑,P层接收V层调用,执行具体的业务逻辑并根据不同的业务逻辑调用不同的Model进行数据操作,当M层执行完数据操作之后通知P层,P层再通过接口回调通知V层进行视图更新。

这样Model、View、Presenter三层都相对独立了,Activity/Fragment也不会像MVC中那样既当爹又当妈,而是专注负则视图操作。以此真正达到了奖Model层与View层解耦的目的。

==MVP的好处之一在于,方便进行单元测试。以往在MVC中,由于Activity同时充当C、V,许多代码都写在Activity中,当要对业务逻辑代码进行测试的时候,必须要部署应用到手机/模拟器上,耗费大量时间。在MVP中,V层与P层独立,它们之间的交互通过interface进行(接口回调),意味着我们可以自定义类实现这个interface来模拟Activity的行为,来进行对P层的单元测试,节省了大量的应用部署时间。==

在实习期间开发的项目,使用的就是MVP模式。使用MVP的最大感受就是Activity中终于不会充斥着大量的业务逻辑代码,而是专注负则UI展示,顿时清爽了,要进行增改都容易了好多。


MVC与MVP的对比

  • MVP中View与Model并不直接交互,而MVC中View与Model是可以直接交互的
  • Presenter与View的交互是通过接口实现的,更有利于进行单元测试
  • 在MVP中Activity代码不臃肿
  • 在MVP中,Presenter可以用于多个视图,但是在MVC中作为Controller的Activity就不行

MVP优点

  • 降低耦合度
  • 模块职责划分明显
  • 利于单元测试
  • 代码复用
  • 便于协同开发

MVP的缺点

  • 多出了许多类、接口,解决方法:
    • 对于逻辑简单的页面可以不适用Presenter,直接在V层处理。在P层中如果不需要处理数据,也可以不适用Model
    • P与V层接口可以复用,因此不需要每个Activity/Fragment都划分一套MVP,可以几个Activity/Fragment使用同一个Presenter/V层接口,当然也可以一个Activity/Fragment根据不同需求持有多个不同类型的Presenter/实现多个V层接口。
  • 由于V层接口可以多次复用,会导致当业务需求变化时,实现了V层接口的Activity/Fragment都需要改动,解决方法:
    • 只能在开发阶段尽量依照面向对象设计中的“开闭原则(OCP)”进行开发,即是对扩展开放,对修改关闭。
  • Presenter持有Activity引用导致Activity销毁后内存无法被回收,出现内存泄漏,解决方法:
    • 是在Presenter中使用弱引用持有对Activity的引用,具体方法是
      1. 定义一个Presenter基类,持有View的弱引用,并在View的onCreate中进行attach,在view的onDestroy中detach
 //V:表示任意view类型,一般是IView
public class BasePresenter<V> {
private WeakReference<V> weakRefView;
public void attach(V view){
        weakRefView = new WeakReference<V>(view);
 }

public void detach() {
    if(isAttach()) {
        weakRefView.clear();
        weakRefView = null;
    }
}
public V obtainView(){
    return isAttach()?weakRefView.get():null;
}

protected boolean isAttach() {
    return weakRefView != null &&
            weakRefView.get() != null;
 }
}

2 定义一个Activity基类,处理与BasePresenter之间的引用关系(值得再研究,因为这样做的话一个Activity就只能自动处理一个Presenter了)

public abstract class BaseActivity<V,P extends BasePresenter<V>> 
extends Activity{

protected P mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mPresenter = createPresenter();
    mPresenter.attach((V)this);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mPresenter.detach();
}

public abstract P createPresenter();

后话

看了这么久MVC、MVP的资料,网上针对它们的具体实现各有不同,一开始还很纠结到底哪个是对的,想着找一些权威资料。后来想通了MVC、MVP这些框架模式不过都是为了解决我们开发中遇到的结构混乱、耦合度高的问题,MVP纵然能实现更好地解耦,但对于简单的页面/业务逻辑还死守标准的MVP模式,徒增一堆类/接口,反倒降低了开发效率。使用框架模式的目的就是为了更好地开发,不能本末倒置增加工作量来满足所谓的标准。