Skip to content

MVP 是《MVC 模式》到《MVVM 模式》之间的过渡形态,Android 早期开发几乎人手一个。它解决的是 MVC 里"Controller 和 View 耦合紧"的问题。这篇把它讲清楚:Presenter 和 Controller 到底差在哪、为什么它更解耦、又为什么后来被 MVVM 取代。

一、解决什么问题:MVC 的 Controller 和 View 耦合

MVC 里 Controller 直接操作 View:

java
// MVC:Controller 直接往 Model 塞数据、指定视图
model.addAttribute("order", order);   // 直接操作 View 的数据
return "order/detail";                 // 直接指定视图

前端一变(视图名改、参数改),Controller 就要跟着改——Controller 和 View 绑在一起

MVP 的思路:View 完全被动,Presenter 通过"接口"操作 View,让 View 和业务彻底隔离。

二、三部分 + Presenter 替代 Controller

部分职责特点
Model数据 + 业务逻辑和 MVC 一样
View纯展示 + 上报用户操作完全被动,不含逻辑
Presenter处理所有逻辑,通过接口操作 View替代 Controller,是核心

关键在 Presenter:它持有 View 的接口引用(不是具体 View),逻辑处理完后,通过接口去更新界面。View 只知道"Presenter 让我显示啥",不知道数据从哪来。

三、数据流

MVP 的数据流,Presenter 是绝对核心:

View 上报事件 → Presenter 处理 → 调 Model → Presenter 拿到结果 → 通过接口更新 View

View 和 Model 完全不认识,中间全靠 Presenter 中转。

四、Android 落地(经典写法)

MVP 在 Android 是经典模式,Activity 就是 View:

java
// ① View 接口:定义界面能做什么
public interface LoginView {
    void showLoading();
    void showSuccess();
    void showError(String msg);
}

// ② Presenter:处理逻辑,通过 View 接口操作界面
public class LoginPresenter {
    private LoginView view;
    private UserModel model = new UserModel();

    public LoginPresenter(LoginView view) { this.view = view; }

    public void login(String name, String pwd) {
        view.showLoading();                    // 通过接口操作 View
        if (model.check(name, pwd)) {          // 调 Model 校验
            view.showSuccess();
        } else {
            view.showError("账号或密码错误");
        }
    }
}

// ③ Activity 实现 View 接口:只负责"显示什么"
public class LoginActivity extends Activity implements LoginView {
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle b) {
        presenter = new LoginPresenter(this);
        findViewById(R.id.login).setOnClickListener(v ->
            presenter.login(getName(), getPwd())   // 只上报事件,不处理逻辑
        );
    }

    public void showLoading() { /* 显示加载中 */ }
    public void showSuccess() { /* 跳转主页 */ }
    public void showError(String msg) { /* 弹错误 */ }
}

关键LoginActivity没有业务逻辑——它只实现 LoginView 接口"显示什么",逻辑全在 LoginPresenter。想测登录逻辑,直接 mock 一个 LoginView 就能单测 Presenter,不用起 Activity。

MVP 数据流:View 被动,Presenter 通过接口操作 View,View 和 Model 彻底隔离

五、MVP vs MVC

维度MVCMVP
View 是否知道 Model知道(会读 Model 数据渲染)不知道(只跟 Presenter 打交道)
View 角色主动读数据展示完全被动,等 Presenter 通过接口投喂
中间角色Controller(协调)Presenter(主导,核心逻辑)
View 和 Model有联系彻底隔离(经 Presenter)
可测试性一般好(Presenter 可脱离 View 单测)

注:MVC 里 View 读 Model 有两种形式——经典 MVC 是 View 订阅 Model(观察者模式),Web MVC(Spring)是 Controller 把数据塞进 Model 容器、View 读容器渲染。但不管是哪种,MVC 的 View 都"知道 Model 的存在";而 MVP 的 View 完全不知道 Model,这是两者的本质区别。

"View 订阅 Model"是什么?会不会很像 MVVM?

确实很像——因为 MVVM 就是从这个机制演化来的,区别只在"手动"还是"自动":

java
// 经典 MVC:View 用观察者模式订阅 Model,收到通知后"手动"更新
class OrderModel {
    private List<OrderView> views = new ArrayList<>();
    private String status;

    void attach(OrderView v) { views.add(v); }         // ① 订阅:View 把自己注册进来
    void setStatus(String s) {
        status = s;
        for (OrderView v : views) v.onModelChanged();  // ③ 通知:状态一变,挨个通知
    }
    String getStatus() { return status; }
}

class OrderView {
    private OrderModel model;                          // View 持有 Model 引用
    private Label label;

    OrderView(OrderModel model) {
        this.model = model;
        model.attach(this);                            // ② 订阅动作:构造时把自己注册进 Model
    }

    void onModelChanged() {                            // ④ 收到通知:手动拉数据 + 更新界面
        label.setText(model.getStatus());
    }
}

// 使用:View 一创建,就自动订阅了 Model
OrderModel model = new OrderModel();
OrderView view = new OrderView(model);   // ← 订阅发生在这里(构造器里 attach)
model.setStatus("已支付");               // ← 状态一变,view.onModelChanged() 被自动调用
java
// MVVM:框架自动绑定,View 不用写 onModelChanged 这套
// <TextView android:text="@{viewModel.status}" />      // 声明绑定
// status 一变,框架自动刷新,View 零更新代码

区别一句话:两者都是"数据变 → View 更新",但经典 MVC 要 View 手动写 onModelChanged(),MVVM 把它做成了框架的自动绑定。所以 MVVM 就是经典 MVC 观察者机制的"框架化版本"(详见《MVVM 模式》)。

顺带看出经典 MVC 的一个痛点:上面 View 直接持有 Model、直接 model.getStatus()View 和 Model 耦合很严重——Model 的字段一改名,View 就得跟着改。而 MVP 让 View 连 Model 的存在都不知道(只依赖 View 接口),正是为了解开这个耦合。

一句话:MVP 把 MVC 里"View 和 Controller 的耦合"解开了,用 Presenter + View 接口彻底隔离

六、MVP 的问题(为什么被 MVVM 取代)

MVP 解耦了,但代价也不小:

  1. 样板代码爆炸:每个界面都要写一个 View 接口 + 一个 Presenter,接口方法一堆(showLoading/showSuccess/showError...),界面一多就写吐
  2. Presenter 要手动更新 View:逻辑里到处 view.showXxx(),更新点一多还是繁琐

这正是《MVVM 模式》要解决的——注意,"手动更新 View"这个痛点 MVC 也有(Controller 手动塞数据),MVP 只是把它换成了 Presenter 手动;MVVM 用"双向绑定"彻底干掉"手动更新"这个体力活。

小结

  • MVP 用 Presenter 替代 Controller,解决 MVC 里 View 和 Controller 耦合的问题
  • View 完全被动(只实现接口),Presenter 是核心(逻辑 + 通过接口操作 View)
  • View 和 Model 彻底隔离,Presenter 可脱离 View 单测
  • 遗留问题:样板代码爆炸、Presenter 手动更新 View → 引出 MVVM

下一步看《MVVM 模式》——它用双向绑定,让 View 自动同步,不再需要 Presenter 一行行手动更新。