Android设计模式之MVP

本文详细介绍了MVP设计模式的概念及其在实际项目中的应用。通过对比MVC模式,阐述了MVP模式如何实现View和Model的完全解耦,增强代码的可维护性和复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发过程中相信大部分人都使用过MVC设计模式,很多人会觉得此模式使用简单,而且在一定程度上使得代码结构清晰、易于维护。但是美中不足的是View视图和Model模型没有达到真正的解耦,如果代码量过大还是会造成代码的过于臃肿、可读性差,因此MVP设计模式就逐步走入了我们的视野中被广大开发者所熟知。

MVP是模型Model-视图View-主持人Presenter的缩写,Model负责建立数据结构和相应的业务逻辑操作;View负责界面数据的显示更新以及用户触发的各类事件响应;Presenter处理程序的各种逻辑分发,是模型与视图之间的桥梁。

MVC设计模式中View视图是可以直接读取Model模型中的数据的,Model模型数据发生改变时会通知View视图的数据显示发生相应的改变。而在MVP设计模式中Model模型和View视图之间是没有直接联系的,它们是两个完全独立的模块,当Model模型发生数据改变时,会通过Presenter主持人通知View视图发生相应的UI改变。因此MVP设计模式相对于MVC设计模式实现了View视图和Model模型的完全分离,也就是说Model模型进行业务数据处理时和View视图数据显示没有任何关联。

代码结构图:

代码结构图

Model模型

1、Model接口

package com.wiggins.mvp.model;

import com.wiggins.mvp.listener.OnLoginListener;

/**
 * @Description 登录操作接口
 * @Author 一花一世界
 * @Time 2017/1/17 16:17
 */

public interface LoginModel {

    void login(String name, String password, OnLoginListener onLoginListener);
}

2、Model接口实现

package com.wiggins.mvp.model.imple;

import com.wiggins.mvp.listener.OnLoginListener;
import com.wiggins.mvp.model.LoginModel;
import com.wiggins.mvp.utils.NetworkUtils;

/**
 * @Description 登录操作接口实现,这里主要是业务逻辑操作
 * @Author 一花一世界
 * @Time 2017/1/17 16:17
 */

public class LoginModelImple implements LoginModel {

    @Override
    public void login(String name, String password, final OnLoginListener onLoginListener) {
        if (name.isEmpty()) {
            onLoginListener.onUsernameEmpty();
        } else if (password.isEmpty()) {
            onLoginListener.onPasswordEmpty();
        } else if (!name.equals("admin")) {
            onLoginListener.onUsernameError();
        } else if (!password.equals("123456")) {
            onLoginListener.onPasswordError();
        } else if (!NetworkUtils.isNetworkAvailable()) {
            onLoginListener.onFailure();
        } else {
            onLoginListener.onSuccess();
        }
    }
}

View视图

1、View接口

package com.wiggins.mvp.view;

import android.content.Context;

/**
 * @Description 登录View接口
 * @Author 一花一世界
 * @Time 2017/1/17 16:18
 */

public interface LoginView {

    //上下文
    Context getContext();

    //登录成功后跳转到首页
    void moveToIndex();

    //Toast提示
    void showToast(String msg);

    //获取界面的用户名
    String getName();

    //获取界面的密码
    String getPassword();
}

2、View接口实现

package com.wiggins.mvp.activity;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.wiggins.mvp.R;
import com.wiggins.mvp.presenter.LoginPresenter;
import com.wiggins.mvp.presenter.imple.LoginPresenterImple;
import com.wiggins.mvp.view.LoginView;

/**
 * @Description mvp登录案例
 * @Author 一花一世界
 * @Time 2017/1/17 17:34
 */

public class LoginActivity extends Activity implements View.OnClickListener, LoginView {

    private LoginActivity mActivity = null;

    private EditText mEdtName;
    private EditText mEdtPwd;
    private Button mBtnLogin;

    private LoginPresenter loginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        mActivity = this;
        loginPresenter = new LoginPresenterImple(this);

        initView();
        setListener();
    }

    private void initView() {
        mEdtName = (EditText) findViewById(R.id.edt_name);
        mEdtPwd = (EditText) findViewById(R.id.edt_pwd);
        mBtnLogin = (Button) findViewById(R.id.btn_login);
    }

    private void setListener() {
        mBtnLogin.setOnClickListener(this);
    }

    @Override
    public Context getContext() {
        return mActivity;
    }

    @Override
    public void moveToIndex() {
        Intent intent = new Intent(mActivity, MainActivity.class);
        startActivity(intent);
        finish();
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(mActivity, msg, Toast.LENGTH_LONG).show();
    }

    @Override
    public String getName() {
        return mEdtName.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return mEdtPwd.getText().toString().trim();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_login:
                loginPresenter.onLoginInfo();
                break;
        }
    }
}

Presenter主持人

1、Presenter接口

package com.wiggins.mvp.presenter;

/**
 * @Description 登录Presenter接口
 * @Author 一花一世界
 * @Time 2017/1/17 17:33
 */

public interface LoginPresenter {

    void onLoginInfo();
}

2、Presenter接口实现

package com.wiggins.mvp.presenter.imple;


import com.wiggins.mvp.R;
import com.wiggins.mvp.listener.OnLoginListener;
import com.wiggins.mvp.model.LoginModel;
import com.wiggins.mvp.model.imple.LoginModelImple;
import com.wiggins.mvp.presenter.LoginPresenter;
import com.wiggins.mvp.view.LoginView;


/**
 * @Description 负责完成登录界面View与Model之间的交互
 * @Author 一花一世界
 * @Time 2017/1/17 17:31
 */

public class LoginPresenterImple implements LoginPresenter {

    private LoginModel loginModel;
    private LoginView loginView;

    public LoginPresenterImple(LoginView loginView) {
        this.loginView = loginView;
        loginModel = new LoginModelImple();
    }

    @Override
    public void onLoginInfo() {
        String name = loginView.getName();
        String password = loginView.getPassword();
        loginModel.login(name, password, new OnLoginListener() {
            @Override
            public void onUsernameEmpty() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.name_not_empty));
            }

            @Override
            public void onPasswordEmpty() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.password_not_empty));
            }

            @Override
            public void onUsernameError() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.name_error));
            }

            @Override
            public void onPasswordError() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.password_error));
            }

            @Override
            public void onSuccess() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.login_successful));
                loginView.moveToIndex();
            }

            @Override
            public void onFailure() {
                loginView.showToast(loginView.getContext().getResources().getString(R.string.login_failed));
            }
        });
    }
}

回调接口

package com.wiggins.mvp.listener;

/**
 * @Description 登录回调接口
 * @Author 一花一世界
 * @Time 2017/1/17 17:35
 */

public interface OnLoginListener {

    //用户名为空
    void onUsernameEmpty();

    //密码为空
    void onPasswordEmpty();

    //用户名错误
    void onUsernameError();

    //密码错误
    void onPasswordError();

    //登录成功
    void onSuccess();

    //登录失败
    void onFailure();
}

ViewModel不直接进行交互,而是采用Presenter作为ViewModel之间交互的桥梁。其中Presenter中同时持有View层以及Model层的Interface引用,而View层持有Presenter层的Interface引用。当View层的某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层进行数据请求,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。

结构图

MVP的优点:

  1. 易于维护:模块职责划分明确,层次清晰;
  2. 低耦合度:实现了ModelView的分离;
  3. 复用性高:一个Presenter可以用于多个View
  4. 健壮稳定:代码容错性、可移植性强;
  5. 灵活性高:新增或修改需求无需更改原本的代码逻辑;

MVP的缺点:

  1. 对于不是很大的项目来说会提高代码的复杂及时间成本;
  2. View视图的渲染放在了Presenter中,所以View视图和Presenter的交互会过于频繁;
  3. 如果Presenter过多地渲染了View视图,会使得它与特定视图的联系过于紧密,一旦视图需要更改,那么Presenter也需要更改了。

项目地址 ☞ 传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值