Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

MVC 与 MVP

MVC

其实Android中只有MV

比如对于登录页面,MVC的基本流程为:

用户与View交互,View接收并反馈用户的动作,View把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图渲染返回的数据。

在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。

比如在Android中,对于登录页面,典型的交互过程是这样的:

用户点击登录按钮 → Activity中注册的监听器检测到点击事件 → Activity通过转动View中的ProgressBar来响应用户点击事件 → Activity通过View中的EditText获取用户输入的账户密码 → Activity将数据交由业务逻辑层(Model层)处理 → Model层处理完成后通过回调将数据返回给Activity → Activity更新UI反馈给用户

由上面的案例可以看出,其实这个View对应于布局文件能做的事情特别少,实际上关于该布局文件中的数据绑定、事件处理的代码都在Activity中,造成了Activity既像View又像Controller,这可能也就是为何:

Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.

MVC经典的路程图:

MVC各层的作用

  • M层:Model,数据模型。负责与数据处理相关的业务逻辑的处理,比如数据库读写操作,网络请求操作,复杂的算法,耗时的任务等。Model是一个应用系统的核心部分,代表了该系统实际要实现的所有功能。当M层完成数据处理后,会通知Controller更新View。
  • V层:View,XML布局、自定义View。负责在屏幕上渲染出相应的图形信息展示给用户看。
  • C层:Controller,控制器,Activity或者Fragment。负责接收如点击、触摸、电话呼入、网络改变等外部事件,并向Model层发送数据请求。同时负责接收Model层处理完数据后发的通知,并更新View。Controller是View和Model之间通信的桥梁。

MVP

让M和V完全解耦

MVP的核心就是:将View和Model完全隔离出来,通过Presenter统一调度管理

特点:

  • 是MVC的演化版本
  • 让Model和View完全解耦,由Presenter负责完成View与Model的交互
  • 将Actvity视为View层,减少了Activity的职责,将复杂的逻辑代码提取到了Presenter中
  • Presenter与View之间的交互完全是通过接口的
  • 代码很清晰,不过增加了很多类;耦合度更低,更方便的进行测试;有助于协同开发,降低维护成本

MVP的基本流程:

Presenter从View中获取需要的参数,交给Model去处理,Model执行过程中的反馈以及结果告诉Presenter,Presenter再让View做对应的显示。

MVP经典的路程图:

MVP各层的作用

  • Model:数据模型,和MVC中的Model一样
  • View:对应UI界面(包括Activity、Fragment、以及所有视图),负责View的绘制以及与用户交互
  • Presenter:调度者,负责完成View和Model间的交互

用MVP架构编写登录模块完整版

定义Presenter接口(可选)

分析这个模块需要哪些业务逻辑,或者说有哪些复杂的功能,以此定义Presenter接口。

对于登录模块,主要的就是登录功能。为了增加接口的复杂度,这里我又添加了一个退出前清理功能。

public interface Login_Presenter_I {
    void login(String username, String password);//登录过程可能涉及到很工作,所以把它抽出来。此过程需要与View交互

    void onFinishActivity();//退出前可能要做很多清理工作,所以也把它抽出来。此过程不需要与View交互
}

定义Model接口(可选)及MP回调接口(必选)

分析上述Presenter层中的功能在被Model层处理过程中,Model需要通知Presenter哪些内容,以此定义Model接口。

Model层在执行过程中,是通过接口通知Presenter执行过程中的状态以及执行完毕后的结果的,为了逻辑更清晰,建议此此接口定义为Model层接口的内部接口。

public interface Login_Model_I {
    //参数 listener:Model层通过此接口通知Presenter执行过程中的状态以及执行完毕后的结果
    void login(String username, String password, OnLoginListener listener);//执行过程中需要通知Presenter

    void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//执行过程中不需要通知Presenter

    //将Model层需要通知Presenter的内容,按照类别,定义在不同的接口中
    interface OnLoginListener {
        void onUsernameError();
        void onPasswordError();
        void onSuccess();
    }
}

定义View接口(必选)

分析所有可能会操作UI的最基础逻辑,以此定义View接口。

Presenter层是通过接口来操作View层的。

public interface Login_Activity_I {
    void showProgress();
    void hideProgress();
    void setUsernameError();
    void setPasswordError();
    void showToast(String msg, int duration);
}

定义Presenter的实现类

public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener {

    private Login_Activity_I loginActivityI; //拿到的是接口,整个Presenter中没有导入任何View和Activity
    private Login_Model_I loginIModel; //拿到的是Model层的实现类

    public Login_Presenter_Impl(Login_Activity_I loginActivityI) {
        this.loginActivityI = loginActivityI;//View层的实现类,由Activity传过来
        this.loginIModel = new Login_Model_Impl();//Model层的实现类,由Activity传过来或自己创建均可
    }

    @Override
    public void login(String username, String password) {
        if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
        loginIModel.login(username, password, this);//交给Model层处理
    }

    @Override
    public void onFinishActivity() {
        if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
        boolean clearSp = new Random().nextBoolean();
        boolean deleteCache = new Random().nextBoolean();
        loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交给Model层处理
        if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI
    }

    @Override
    public void onUsernameError() {
        if (loginActivityI != null) {
            loginActivityI.setUsernameError();
            loginActivityI.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (loginActivityI != null) {
            loginActivityI.setPasswordError();
            loginActivityI.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (loginActivityI != null) loginActivityI.showToast("登录成功", Toast.LENGTH_SHORT);
    }
}

定义Model的实现类

Model层的实现类,只处理逻辑,完全不操作View,对逻辑处理的状态反馈给Presenter

public class Login_Model_Impl implements Login_Model_I {
    @Override
    public void login(String username, String password, OnLoginListener listener) {
        if (TextUtils.isEmpty(username)) listener.onUsernameError();
        else if (TextUtils.isEmpty(password)) listener.onPasswordError();
        else listener.onSuccess();
    }

    @Override
    public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) {
        if (clearSp) Log.i("bqt", "【清理SP】");
        if (deleteCache) Log.i("bqt", "【清理缓存】");
    }
}

让Activity实现View接口

public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;

    private Login_Presenter_I presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);

        presenter = new Login_Presenter_Impl(this);//new一个Presenter的实现类,把自己传过去。实际上接收的只是LoginView接口的实例
    }

    @Override
    public void finish() {
        presenter.onFinishActivity();
        super.finish();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void showToast(String msg, int duration) {
        Toast.makeText(this, msg, duration).show();
    }

    @Override
    public void onClick(View v) {
        //点击登录时,View把数据传给presenter,presenter处理完数据后通知View处理事件
        presenter.login(username.getText().toString(), password.getText().toString());
    }
}

用MVP架构编写登录模块简洁版

View层接口

public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

Activity

public class LoginActivity extends AppCompatActivity implements LoginView {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = findViewById(R.id.progress);
        username = findViewById(R.id.username);
        password = findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(v -> login());

        presenter = new LoginPresenter(this, new LoginModel());
    }

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

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError("用户名错误");
    }

    @Override
    public void setPasswordError() {
        password.setError("密码错误");
    }

    @Override
    public void navigateToHome() {
        startActivity(new Intent(this, MainActivity.class));
        finish();
    }

    private void login() {
        presenter.login(username.getText().toString(), password.getText().toString());
    }
}

Presenter

public class LoginPresenter implements LoginModel.OnLoginFinishedListener {

    private LoginView LoginView;
    private LoginModel loginModel;

    LoginPresenter(LoginView LoginView, LoginModel loginModel) {
        this.LoginView = LoginView;
        this.loginModel = loginModel;
    }

    public void login(String username, String password) {
        if (LoginView != null) {
            LoginView.showProgress();
        }
        loginModel.login(username, password, this);
    }

    public void onDestroy() {
        LoginView = null;
    }

    @Override
    public void onUsernameError() {
        if (LoginView != null) {
            LoginView.setUsernameError();
            LoginView.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (LoginView != null) {
            LoginView.setPasswordError();
            LoginView.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (LoginView != null) {
            LoginView.navigateToHome();
        }
    }
}

Model

public class LoginModel {

    interface OnLoginFinishedListener {
        void onUsernameError();

        void onPasswordError();

        void onSuccess();
    }

    public void login(final String username, final String password, final OnLoginFinishedListener listener) {
        new Handler().postDelayed(() -> {
            if (TextUtils.isEmpty(username)) {
                listener.onUsernameError();
                return;
            }
            if (TextUtils.isEmpty(password)) {
                listener.onPasswordError();
                return;
            }
            listener.onSuccess();
        }, 2000);
    }
}

2017-9-14

MVC 与 MVP 架构 MD的更多相关文章

  1. Android App的架构设计:从VM、MVC、MVP到MVVM

    随着Android应用开发规模的扩大,客户端业务逻辑也越来越复杂,已然不是简单的数据展示了.如同后端开发遇到瓶颈时采用的组件拆分思想,客户端也需要进行架构设计,拆分视图和数据,解除模块之间的耦合,提高 ...

  2. 浅谈MVC、MVP、MVVM架构模式的区别和联系

    MVC.MVP.MVVM这些模式是为了解决开发过程中的实际问题而提出来的,目前作为主流的几种架构模式而被广泛使用. 一.MVC(Model-View-Controller) MVC是比较直观的架构模式 ...

  3. js架构设计模式——MVC,MVP 和 MVVM 的图示及简单明了的区别说明

    MVC,MVP 和 MVVM 的图示 复杂的软件必须有清晰合理的架构,否则无法开发和维护. MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用.它本身很容易理 ...

  4. js架构设计模式——你对MVC、MVP、MVVM 三种组合模式分别有什么样的理解?

    你对MVC.MVP.MVVM 三种组合模式分别有什么样的理解? MVC(Model-View-Controller)MVP(Model-View-Presenter)MVVM(Model-View-V ...

  5. 架构 : 三层架构、MVC、MVP、MVVM

    1. 三层架构   将整个业务应用划分为:界面层(User Interface layer, UIL).业务逻辑层(Business Logic Layer, BLL).数据访问层(Data acce ...

  6. Android 程序架构: MVC、MVP、MVVM、Unidirectional、Clean...

    摘选自:GUI 应用程序架构的十年变迁:MVC.MVP.MVVM.Unidirectional.Cleanhttps://zhuanlan.zhihu.com/p/26799645 MV* in An ...

  7. MVC、MVP、MVVM架构模式

    MVC模式 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,不仅适用于 ...

  8. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM

    刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...

  9. MVC、MVP、MVVM、Angular.js、Knockout.js、Backbone.js、React.js、Ember.js、Avalon.js、Vue.js 概念摘录

    注:文章内容都是摘录性文字,自己阅读的一些笔记,方便日后查看. MVC MVC(Model-View-Controller),M 是指业务模型,V 是指用户界面,C 则是控制器,使用 MVC 的目的是 ...

随机推荐

  1. linux 添加samba账户

    1.adduser kilen   添加linux账户 2.cd /etc/samba/ 当前目录下修改smb.conf 文件 ,一般情况下是只读文件,需要修改权下 (用root用户) chmod 7 ...

  2. rabbitmq学习(二) —— helloword!

    rabbitmq学习当然是跟着官网走最好了,官网的教程写的很好,跟着官网教程走一遍就会有个初步了解了 下面的教程转自http://cmsblogs.com/?p=2768,该博客对官网的翻译还不错 介 ...

  3. 智能家居实践(番外篇)—— 接入 HomeKit 实现用 Siri 控制家电

    转载:智能家居实践(番外篇)—— 接入 HomeKit 实现用 Siri 控制家电 前面我写了一个系列共三篇的智能家居实践,用的是 Amazon Echo 实现语音控制,但是 Amazon Echo ...

  4. Django小知识点整理

    一.Django中Choices字段显示中文的两种方式 1.get_xxxx_display():这种方式只能针对对象来使用 {% for row in server_list %} <li&g ...

  5. 《jQuery基础教程》读书笔记

    最近在看<jQuery基础教程>这本书,做了点读书笔记以备回顾,不定期更新. 第一章第二章比较基础,就此略过了... 第三章 事件 jQuery中$(document).ready()与j ...

  6. POJ 1469 COURSES 二分图最大匹配 二分图

    http://poj.org/problem?id=1469 这道题我绝壁写过但是以前没有mark过二分图最大匹配的代码mark一下. 匈牙利 O(mn) #include<cstdio> ...

  7. python开发_webbrowser_浏览器控制模块

    ''' python的webbrowser模块支持对浏览器进行一些操作 主要有以下三个方法: webbrowser.open(url, new=0, autoraise=True) webbrowse ...

  8. tyvj 1004 滑雪 记忆化搜索

    滑雪 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.tyvj.cn/p/1004 Description     trs喜欢滑雪.他来 ...

  9. mysql另类分页方法

    mysql> limit ,;select found_rows(); +----+-----------------+---------+--------+ | id | rankName | ...

  10. 【原】MySQL实用SQL积累

    [文档简述] 本文档用来记录一些常用的SQL语句,以达到快速查询的目的. [常用SQL] 1.mysql数据库中获取某个表的所有字段名 select COLUMN_NAME from informat ...