# MVP架构

## 目录
- [MVP模式介绍](#mvp模式介绍)
- [MVP三层职责](#mvp三层职责)
- [MVP实现示例](#mvp实现示例)
- [MVP优缺点](#mvp优缺点)
- [MVP与MVC对比](#mvp与mvc对比)
- [MVP最佳实践](#mvp最佳实践)
- [面试常见问题](#面试常见问题)

---

## MVP模式介绍

### MVP 概念

MVP（Model-View-Presenter）是一种软件架构模式，是 MVC 的改进版本：

- **Model**：数据和业务逻辑
- **View**：用户界面（通过接口与 Presenter 通信）
- **Presenter**：处理业务逻辑，协调 Model 和 View

### MVP 架构图

```
┌─────────┐
│  View   │  ←─── 显示数据（通过接口）
└────┬────┘
     │ 用户交互
     ↓
┌─────────┐
│Presenter │  ←─── 处理业务逻辑
└────┬────┘
     │ 更新
     ↓
┌─────────┐
│  Model  │  ←─── 数据和业务逻辑
└─────────┘
```

### MVP 数据流

1. **用户操作 View** → View 调用 Presenter 方法
2. **Presenter 处理** → 调用 Model 获取数据
3. **Model 返回数据** → Presenter 处理数据
4. **Presenter 更新 View** → 通过接口更新 View

### MVP 核心特点

1. **View 和 Model 完全解耦**：View 不直接访问 Model
2. **View 通过接口与 Presenter 通信**：便于测试和替换
3. **Presenter 持有 View 接口引用**：不持有具体 View 实现
4. **业务逻辑在 Presenter 中**：View 只负责显示

---

## MVP三层职责

### Model 层

**职责：**
- 数据模型定义
- 业务逻辑处理
- 数据访问（网络、数据库、文件等）
- 数据验证

**特点：**
- 不依赖 View 和 Presenter
- 可以独立测试
- 可以被多个 Presenter 使用

### View 层

**职责：**
- 显示数据
- 处理用户输入
- 提供 UI 反馈（加载、错误提示等）

**特点：**
- 通过接口与 Presenter 通信
- 不包含业务逻辑
- 被动接收 Presenter 的更新

### Presenter 层

**职责：**
- 处理用户交互
- 调用 Model 获取数据
- 处理业务逻辑
- 更新 View（通过接口）

**特点：**
- 持有 View 接口引用
- 不持有具体 View 实现
- 可以独立测试（使用 Mock View）

---

## MVP实现示例

### 基础 MVP 实现

```java
// Model：用户数据模型
public class User {
    private String id;
    private String name;
    private String email;
    
    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    // Getters
    public String getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
}

// Model：用户业务逻辑
public class UserRepository {
    private List<User> users = new ArrayList<>();
    
    public void addUser(User user, Callback callback) {
        // 模拟网络请求
        new Thread(() -> {
            try {
                Thread.sleep(1000); // 模拟网络延迟
                users.add(user);
                callback.onSuccess();
            } catch (InterruptedException e) {
                callback.onError(e.getMessage());
            }
        }).start();
    }
    
    public void getUsers(Callback<List<User>> callback) {
        new Thread(() -> {
            try {
                Thread.sleep(500);
                callback.onSuccess(users);
            } catch (InterruptedException e) {
                callback.onError(e.getMessage());
            }
        }).start();
    }
    
    interface Callback<T> {
        void onSuccess(T data);
        void onError(String error);
    }
}

// View 接口
public interface UserView {
    void showLoading();
    void hideLoading();
    void showUsers(List<User> users);
    void showError(String error);
    void showSuccess(String message);
    void clearInput();
}

// Presenter
public class UserPresenter {
    private UserView view;
    private UserRepository repository;
    
    public UserPresenter(UserView view, UserRepository repository) {
        this.view = view;
        this.repository = repository;
    }
    
    public void addUser(String name, String email) {
        if (name.isEmpty() || email.isEmpty()) {
            view.showError("Name and Email cannot be empty");
            return;
        }
        
        view.showLoading();
        User user = new User(UUID.randomUUID().toString(), name, email);
        repository.addUser(user, new UserRepository.Callback<Void>() {
            @Override
            public void onSuccess(Void data) {
                view.hideLoading();
                view.showSuccess("User added successfully");
                view.clearInput();
                loadUsers();
            }
            
            @Override
            public void onError(String error) {
                view.hideLoading();
                view.showError(error);
            }
        });
    }
    
    public void loadUsers() {
        view.showLoading();
        repository.getUsers(new UserRepository.Callback<List<User>>() {
            @Override
            public void onSuccess(List<User> data) {
                view.hideLoading();
                view.showUsers(data);
            }
            
            @Override
            public void onError(String error) {
                view.hideLoading();
                view.showError(error);
            }
        });
    }
    
    public void onDestroy() {
        view = null; // 防止内存泄漏
    }
}

// View 实现：Activity
public class UserActivity extends AppCompatActivity implements UserView {
    private EditText etName, etEmail;
    private Button btnAdd;
    private RecyclerView rvUsers;
    private UserPresenter presenter;
    private UserAdapter adapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        
        initViews();
        initPresenter();
        setupRecyclerView();
        setupListeners();
        
        presenter.loadUsers();
    }
    
    private void initViews() {
        etName = findViewById(R.id.etName);
        etEmail = findViewById(R.id.etEmail);
        btnAdd = findViewById(R.id.btnAdd);
        rvUsers = findViewById(R.id.rvUsers);
    }
    
    private void initPresenter() {
        UserRepository repository = new UserRepository();
        presenter = new UserPresenter(this, repository);
    }
    
    private void setupRecyclerView() {
        adapter = new UserAdapter(new ArrayList<>());
        rvUsers.setLayoutManager(new LinearLayoutManager(this));
        rvUsers.setAdapter(adapter);
    }
    
    private void setupListeners() {
        btnAdd.setOnClickListener(v -> {
            String name = etName.getText().toString();
            String email = etEmail.getText().toString();
            presenter.addUser(name, email);
        });
    }
    
    // 实现 UserView 接口
    @Override
    public void showLoading() {
        // 显示加载提示
    }
    
    @Override
    public void hideLoading() {
        // 隐藏加载提示
    }
    
    @Override
    public void showUsers(List<User> users) {
        adapter.updateUsers(users);
    }
    
    @Override
    public void showError(String error) {
        Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void showSuccess(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void clearInput() {
        etName.setText("");
        etEmail.setText("");
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) {
            presenter.onDestroy();
        }
    }
}
```

### 使用 RxJava 的 MVP 实现

```java
// Presenter with RxJava
public class UserPresenter {
    private UserView view;
    private UserRepository repository;
    private CompositeDisposable disposables = new CompositeDisposable();
    
    public UserPresenter(UserView view, UserRepository repository) {
        this.view = view;
        this.repository = repository;
    }
    
    public void addUser(String name, String email) {
        if (name.isEmpty() || email.isEmpty()) {
            view.showError("Name and Email cannot be empty");
            return;
        }
        
        view.showLoading();
        User user = new User(UUID.randomUUID().toString(), name, email);
        
        Disposable disposable = repository.addUser(user)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                () -> {
                    view.hideLoading();
                    view.showSuccess("User added successfully");
                    view.clearInput();
                    loadUsers();
                },
                error -> {
                    view.hideLoading();
                    view.showError(error.getMessage());
                }
            );
        
        disposables.add(disposable);
    }
    
    public void loadUsers() {
        view.showLoading();
        Disposable disposable = repository.getUsers()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                users -> {
                    view.hideLoading();
                    view.showUsers(users);
                },
                error -> {
                    view.hideLoading();
                    view.showError(error.getMessage());
                }
            );
        
        disposables.add(disposable);
    }
    
    public void onDestroy() {
        disposables.clear();
        view = null;
    }
}
```

---

## MVP优缺点

### 优点

1. **View 和 Model 完全解耦**：View 不直接访问 Model
2. **易于测试**：Presenter 可以独立测试，使用 Mock View
3. **代码复用**：Presenter 可以被多个 View 使用
4. **职责清晰**：每层职责明确
5. **便于维护**：代码结构清晰，易于维护

### 缺点

1. **代码量增加**：需要定义接口，代码量增加
2. **接口过多**：每个 View 都需要定义接口
3. **Presenter 可能臃肿**：复杂业务逻辑可能导致 Presenter 臃肿
4. **生命周期管理**：需要手动管理 Presenter 的生命周期

---

## MVP与MVC对比

### 主要区别

| 特性 | MVC | MVP |
|------|-----|-----|
| View 和 Model | 耦合 | 完全解耦 |
| View 访问 Model | 可以直接访问 | 不能直接访问 |
| Controller/Presenter | 处理输入和更新 | 处理业务逻辑 |
| 测试性 | 难以测试 | 易于测试 |
| 代码量 | 较少 | 较多 |
| 适用场景 | 小型项目 | 中大型项目 |

### 代码对比

```java
// MVC：View 直接访问 Model
public class MainActivity extends AppCompatActivity {
    private UserRepository repository;
    
    private void loadUsers() {
        repository.getUsers(users -> {
            // View 直接处理 Model 数据
            adapter.updateUsers(users);
        });
    }
}

// MVP：View 通过 Presenter 访问 Model
public class MainActivity extends AppCompatActivity implements UserView {
    private UserPresenter presenter;
    
    private void loadUsers() {
        presenter.loadUsers(); // 通过 Presenter
    }
    
    @Override
    public void showUsers(List<User> users) {
        // Presenter 通过接口更新 View
        adapter.updateUsers(users);
    }
}
```

---

## MVP最佳实践

### 1. View 接口设计

```java
// ✅ 好的接口设计：职责单一
public interface UserView {
    void showUsers(List<User> users);
    void showError(String error);
    void showLoading();
    void hideLoading();
}

// ❌ 不好的接口设计：职责过多
public interface UserView {
    void showUsers(List<User> users);
    void showError(String error);
    void showLoading();
    void hideLoading();
    void navigateToDetail(); // 导航不应该在 View 接口中
    void requestPermission(); // 权限不应该在 View 接口中
}
```

### 2. Presenter 生命周期管理

```java
public class UserPresenter {
    private UserView view;
    
    public void attachView(UserView view) {
        this.view = view;
    }
    
    public void detachView() {
        this.view = null;
    }
    
    public boolean isViewAttached() {
        return view != null;
    }
    
    private void updateView(Runnable update) {
        if (isViewAttached()) {
            update.run();
        }
    }
}

// Activity 中使用
public class UserActivity extends AppCompatActivity implements UserView {
    private UserPresenter presenter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = new UserPresenter();
        presenter.attachView(this);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();
    }
}
```

### 3. 使用 Contract 接口

```java
// 将 View 和 Presenter 接口放在一起
public interface UserContract {
    interface View {
        void showUsers(List<User> users);
        void showError(String error);
        void showLoading();
        void hideLoading();
    }
    
    interface Presenter {
        void loadUsers();
        void addUser(String name, String email);
        void attachView(View view);
        void detachView();
    }
}

// 实现
public class UserPresenter implements UserContract.Presenter {
    private UserContract.View view;
    // ...
}

public class UserActivity extends AppCompatActivity implements UserContract.View {
    private UserContract.Presenter presenter;
    // ...
}
```

### 4. 使用 BasePresenter 和 BaseView

```java
// BaseView
public interface BaseView {
    void showLoading();
    void hideLoading();
    void showError(String error);
}

// BasePresenter
public class BasePresenter<V extends BaseView> {
    protected V view;
    
    public void attachView(V view) {
        this.view = view;
    }
    
    public void detachView() {
        this.view = null;
    }
    
    public boolean isViewAttached() {
        return view != null;
    }
}

// 使用
public interface UserView extends BaseView {
    void showUsers(List<User> users);
}

public class UserPresenter extends BasePresenter<UserView> {
    // ...
}
```

---

## 面试常见问题

### Q1: 什么是 MVP 模式？

**答案：**
MVP（Model-View-Presenter）是一种软件架构模式：
- **Model**：数据和业务逻辑
- **View**：用户界面（通过接口与 Presenter 通信）
- **Presenter**：处理业务逻辑，协调 Model 和 View

### Q2: MVP 和 MVC 的区别？

**答案：**
- **MVC**：View 可以直接访问 Model，Controller 处理输入和更新
- **MVP**：View 和 Model 完全解耦，View 通过接口与 Presenter 通信，Presenter 处理业务逻辑

### Q3: MVP 的优点？

**答案：**
1. View 和 Model 完全解耦
2. 易于测试（Presenter 可以独立测试）
3. 代码复用（Presenter 可以被多个 View 使用）
4. 职责清晰
5. 便于维护

### Q4: MVP 的缺点？

**答案：**
1. 代码量增加（需要定义接口）
2. 接口过多
3. Presenter 可能臃肿
4. 需要手动管理生命周期

### Q5: 如何防止 Presenter 内存泄漏？

**答案：**
1. 在 onDestroy 中调用 `presenter.detachView()`
2. Presenter 持有 View 接口的弱引用
3. 使用 `isViewAttached()` 检查 View 是否可用
4. 及时取消网络请求和订阅

### Q6: MVP 中如何测试 Presenter？

**答案：**
1. 创建 Mock View 实现 View 接口
2. 创建 Mock Model/Repository
3. 测试 Presenter 的业务逻辑
4. 验证 Presenter 是否正确调用 View 方法

---

## 总结

MVP 是 Android 开发中常用的架构模式，通过 View 接口实现 View 和 Model 的解耦，使代码更易于测试和维护。虽然代码量会增加，但对于中大型项目来说，这种结构化的方式更有利于长期维护。

---

*最后更新：2024年*
