区块链技术博客
www.b2bchain.cn

025 | 正确认识MVP&MVVM求职学习资料

D0b2wT.gif

本文介绍了025 | 正确认识MVP&MVVM求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

前一篇文章我们已经详细介绍了 MVC 模式的起源、通信机制和变种,以及在实际应用中的问题。如果对 MVC 理解还不透彻,建议先翻回去看前一篇文章。理解了 MVC 之后,MVP 和 MVVM 相对就容易理解了,下面我们就来深入理解这两个模式。

MVP

对 MVP 模式最早的解说来源于一篇 1996 年时发表的论文:《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》 。论文的作者是一个叫 Mike Potel 的人,当时是 Taligent 公司的 VP & CTO,Taligent 则是 IBM 的全资子公司。不过,如果你看过这篇论文的内容,就会发现,其实,最原始的 MVP 与我们现在所认识的 MVP 并不一样。论文中所论述的 MVP 其实是下面这样的结构:

025 | 正确认识MVP&MVVM

该 MVP 其实是从数据管理用户界面两个维度的几个问题出发,将 Smalltalk 版本的 MVC 进行再分解演化而成,拆分出了几个中间组件:Interactor、Commands、Selections。Interactor 最好理解,其实就是 View 的交互事件。Commands 定义了对 Model 选定数据的一些操作,如删除、修改、保存等操作。Selections 可以理解为就是对 Model 数据集的筛选过滤,根据条件取子集。另外,所有的组件,也包括 Model、View、Presenter,都是通过接口进行交互的。

如果忽略掉三件套之间的几个中间组件,你就会发现,它们之间的依赖关系其实和 Smalltalk 版本的 MVC 是一样的。论文中也有提到,Presenter 其实就是 Controller,只是为了与 MVC 区别开来,所以才称为 Presenter

再来看看我们现在所熟知的 MVP 结构图:

025 | 正确认识MVP&MVVM

看到这关系图,你还会发现,这和前篇文章说的变种 MVC 不是一模一样吗?没错,关系图的确是一样的,但背后的实现和角色划分却不太一样,我们后面就讲。至于,这种模式的 MVP 是如何演化而来的,我也不得而知,只知道这已经成为了当代 MVP 的标准结构。

在 MVP 里,三件套各自的职责和依赖关系和变种 MVC 里的职责和依赖关系其实是一样的,但不同的是,MVP 之间的交互主要是通过接口实现的,Model、View、Presenter 都有各自的接口,定义各自的行为方法。针对接口编程,自然就能减低耦合,提高可复用性,以及容易进行单元测试。

之前我们说过,实际应用中的 MVC,UIViewControllerActivity 其实是同时担任了 Controller 和部分 View 的角色的,职责划分不明确,才导致 UIViewController 和 Activity 的代码混乱不堪,越来越臃肿。而采用 MVP 模式,UIViewController 和 Activity 就明确地划分为 View 角色了,原有的 Controller 角色的职责则交由 Presenter 负责,职责清晰了,代码自然也容易清晰了。

MVP 的简单使用

我们就以一个简单的登录案例来说明如何使用 MVP,下图是该案例的类图:

025 | 正确认识MVP&MVVM

定义了 4 个接口和 3 个实现类,其中,LoginActivity 是 Android 的 Activity 类,在 iOS 中,则可定义为 LoginViewController。LoginActivity 实现了 LoginView 接口,同时还会持有一个 LoginPresenter 对象。LoginView 和 LoginActivity 都明确划分到 View 层,LoginView 定义了登录流程中涉及到的几个UI层的接口方法,包括显示和隐藏加载框,以及登录失败时的错误信息展示,和登录成功后的处理,这些方法都会在 LoginPresenterImpl 的方法中被调用。Presenter 层定义了两个接口,LoginPresenter 只有一个登录的接口方法,OnLoginFinishedListener 则定义了登录结果的回调方法,两个接口统一由 LoginPresenterImpl 来实现即可。另外,从图中也可看到,LoginPresenterImpl 既持有一个 LoginView 对象,也持有一个 LoginModel 对象,LoginPresenterImpl 其实就是 LoginView 和 LoginModel 之间交互的桥梁。而 Model 层的 LoginModel 则不直接持有 LoginPresenter 对象,只在登录接口中加了一个回调对象的参数。

LoginActivity

下面,我们来简单看看一些关键代码的实现。先来看看 LoginActivity 的关键代码:

public class LoginActivity extends AppCompatActivity implements LoginView {     private LoginPresenter loginPresenter;       private ProgressBar progressBar;       private LoginButton loginBtn;       private EditText userNameEdt;       private EditText passwordEdt;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_login);           progressBar = findViewById(R.id.loading);           loginBtn = findViewById(R.id.login);           userNameEdt = findViewById(R.id.username);           passwordEdt = findViewById(R.id.password);          loginPresenter = new LoginPresenterImpl();           loginPresenter.attachView(this);            loginBtn.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 loginPresenter.login(userNameEdt.getText().toString(),                         passwordEdt.getText().toString());             }         });     }        @Override     public void showLoading() {         if (!progressBar.isShowing()) {             progressBar.show();         }     }      @Override     public void hideLoading() {         if (progressBar.isShowing()) {             progressBar.dismiss();         }     }      @Override     public void onLoginError(String msg) {         Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();     }        @Override     public void onLoginSuccess() {         Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();           startActivity(new Intent(this, MainActivity.class));     }        @Override     protected void onDestroy() {         super.onDestroy();         loginPresenter.detachView();     } }

代码逻辑比较简单,从代码中可知,在 onCreate() 方法里初始化了 loginPresenter 对象,并绑定了自身作为 LoginView 的引用,并在登录按钮的点击事件中调用了 loginPresenter.login() 方法,从而将登录事件传递给了 loginPresenter。

LoginPresenterImpl

Presenter 就是 View 和 Model 之间的桥梁,我们来看看 LoginPresenterImpl 的关键代码:

public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {       private LoginView loginView;       private LoginModel loginModel;        public LoginPresenterImpl() {           this.loginModel = new LoginModelImpl();     }        @Override       public void attachView(LoginView loginView) {         this.loginView = loginView;     }        @Override     public void detachView() {         this.loginView = null;     }        @Override     public boolean isViewAttached(){         return loginView != null;     }        @Override     public void login(String username, String password) {           loginView.showLoading();           loginModel.login(username, password, this);     }        @Override     public void onLoginError(String msg) {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginError(msg);         }     }        @Override     public void onLoginSuccess() {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginSuccess();         }     } }

LoginModelImpl

最后,LoginModelImpl 就是对登录的业务逻辑处理了,一般是通过网络请求服务器实现登录逻辑,我们来看看伪代码:

public class LoginModelImpl implements LoginModel {       @Override       public void login(String username, String password, OnLoginFinishedListener listener) {           apiService.login(username, password)               .subscribeOn(Schedulers.newThread())               .observeOn(AndroidSchedulers.mainThread())               .subscribe(new Subscriber<Response>() {                     @Override                     public void onCompleted() {                     }                      @Override                     public void onError(Throwable e) {                           listener.onLoginError("Server Error")                     }                      @Override                     public void onNext(Response res) {                           if (res.isSuccess()) {                           listener.onLoginSuccess();                     } else {                           listener.onLoginError(res.getMsg());                     }                     }               });     } }

该示例代码用到了 Retrofit + RxJava,最核心的代码就在 onNext() 方法里,这是请求响应返回后被调用的方法。

至此,MVP 最简单的使用案例就讲解完了。另外,由于每一个业务功能基本都有对应的一套 MVP 的接口,因此,很多时候会将这一套接口封装在一起,组成一套契约,如下所示:

public interface LoginContract {       public interface LoginView {           ...     }        public interface LoginPresenter {           ...     }        public interface OnLoginFinishedListener {           ...     }        public interface LoginModel {           ...     } }

至此,对 MVP 最简单的用法就演示到这里结束。在实际应用中,应该做一些调整,比如抽离出一个 Base 层,将一些冗余的重复代码封装到对应的 Base 接口或类中,比如抽离出 BaseView、BaseActivity、BasePresenter、BaseListener 等。以及,设计上每一套 MVP 尽量设计成实现单一功能,以便能被复用,从而无需每一个页面都编写单独一套 MVP,尽量使用组合或继承的方式复用 MVP。

MVP 的优缺点

接着,我们来总结下 MVP 有哪些优缺点。先有看看有哪些优点呢:

  1. 我们知道,MVC 模式在 App 实际应用中,Activity 和 UIViewController 既同时担任 Controller 又担任部分 View 的职责,职责不清,导致 Activity 和 UIViewController 容易变得越来越臃肿。而应用 MVP 模式,直接将 Activity 和 UIViewController 划分到 View 层了,职责明确了,自然也避免了 Activity 和 UIViewController 臃肿的问题。
  2. MVP 之间的交互通过接口来进行的,那就便于进行单元测试了,维护性和扩展性也提高了。
  3. M 和 V 之间彻底分离了,降低了耦合性,修改 V 层也不会影响 M 层。

不过,相应地,相比 MVC 也引入了一些弊端:

  1. 由于增加了很多接口的定义,需要编写的代码量暴增,增加了项目的复杂度。
  2. 需要对很多业务模块之间的交互抽象成接口定义,对开发人员的设计能力要求更高了。

前一篇文章我们已经详细介绍了 MVC 模式的起源、通信机制和变种,以及在实际应用中的问题。如果对 MVC 理解还不透彻,建议先翻回去看前一篇文章。理解了 MVC 之后,MVP 和 MVVM 相对就容易理解了,下面我们就来深入理解这两个模式。

MVP

对 MVP 模式最早的解说来源于一篇 1996 年时发表的论文:《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》 。论文的作者是一个叫 Mike Potel 的人,当时是 Taligent 公司的 VP & CTO,Taligent 则是 IBM 的全资子公司。不过,如果你看过这篇论文的内容,就会发现,其实,最原始的 MVP 与我们现在所认识的 MVP 并不一样。论文中所论述的 MVP 其实是下面这样的结构:

025 | 正确认识MVP&amp;MVVM

该 MVP 其实是从数据管理用户界面两个维度的几个问题出发,将 Smalltalk 版本的 MVC 进行再分解演化而成,拆分出了几个中间组件:Interactor、Commands、Selections。Interactor 最好理解,其实就是 View 的交互事件。Commands 定义了对 Model 选定数据的一些操作,如删除、修改、保存等操作。Selections 可以理解为就是对 Model 数据集的筛选过滤,根据条件取子集。另外,所有的组件,也包括 Model、View、Presenter,都是通过接口进行交互的。

如果忽略掉三件套之间的几个中间组件,你就会发现,它们之间的依赖关系其实和 Smalltalk 版本的 MVC 是一样的。论文中也有提到,Presenter 其实就是 Controller,只是为了与 MVC 区别开来,所以才称为 Presenter

再来看看我们现在所熟知的 MVP 结构图:

025 | 正确认识MVP&amp;MVVM

看到这关系图,你还会发现,这和前篇文章说的变种 MVC 不是一模一样吗?没错,关系图的确是一样的,但背后的实现和角色划分却不太一样,我们后面就讲。至于,这种模式的 MVP 是如何演化而来的,我也不得而知,只知道这已经成为了当代 MVP 的标准结构。

在 MVP 里,三件套各自的职责和依赖关系和变种 MVC 里的职责和依赖关系其实是一样的,但不同的是,MVP 之间的交互主要是通过接口实现的,Model、View、Presenter 都有各自的接口,定义各自的行为方法。针对接口编程,自然就能减低耦合,提高可复用性,以及容易进行单元测试。

之前我们说过,实际应用中的 MVC,UIViewControllerActivity 其实是同时担任了 Controller 和部分 View 的角色的,职责划分不明确,才导致 UIViewController 和 Activity 的代码混乱不堪,越来越臃肿。而采用 MVP 模式,UIViewController 和 Activity 就明确地划分为 View 角色了,原有的 Controller 角色的职责则交由 Presenter 负责,职责清晰了,代码自然也容易清晰了。

MVP 的简单使用

我们就以一个简单的登录案例来说明如何使用 MVP,下图是该案例的类图:

025 | 正确认识MVP&amp;MVVM

定义了 4 个接口和 3 个实现类,其中,LoginActivity 是 Android 的 Activity 类,在 iOS 中,则可定义为 LoginViewController。LoginActivity 实现了 LoginView 接口,同时还会持有一个 LoginPresenter 对象。LoginView 和 LoginActivity 都明确划分到 View 层,LoginView 定义了登录流程中涉及到的几个UI层的接口方法,包括显示和隐藏加载框,以及登录失败时的错误信息展示,和登录成功后的处理,这些方法都会在 LoginPresenterImpl 的方法中被调用。Presenter 层定义了两个接口,LoginPresenter 只有一个登录的接口方法,OnLoginFinishedListener 则定义了登录结果的回调方法,两个接口统一由 LoginPresenterImpl 来实现即可。另外,从图中也可看到,LoginPresenterImpl 既持有一个 LoginView 对象,也持有一个 LoginModel 对象,LoginPresenterImpl 其实就是 LoginView 和 LoginModel 之间交互的桥梁。而 Model 层的 LoginModel 则不直接持有 LoginPresenter 对象,只在登录接口中加了一个回调对象的参数。

LoginActivity

下面,我们来简单看看一些关键代码的实现。先来看看 LoginActivity 的关键代码:

public class LoginActivity extends AppCompatActivity implements LoginView {     private LoginPresenter loginPresenter;       private ProgressBar progressBar;       private LoginButton loginBtn;       private EditText userNameEdt;       private EditText passwordEdt;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_login);           progressBar = findViewById(R.id.loading);           loginBtn = findViewById(R.id.login);           userNameEdt = findViewById(R.id.username);           passwordEdt = findViewById(R.id.password);          loginPresenter = new LoginPresenterImpl();           loginPresenter.attachView(this);            loginBtn.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 loginPresenter.login(userNameEdt.getText().toString(),                         passwordEdt.getText().toString());             }         });     }        @Override     public void showLoading() {         if (!progressBar.isShowing()) {             progressBar.show();         }     }      @Override     public void hideLoading() {         if (progressBar.isShowing()) {             progressBar.dismiss();         }     }      @Override     public void onLoginError(String msg) {         Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();     }        @Override     public void onLoginSuccess() {         Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();           startActivity(new Intent(this, MainActivity.class));     }        @Override     protected void onDestroy() {         super.onDestroy();         loginPresenter.detachView();     } }

代码逻辑比较简单,从代码中可知,在 onCreate() 方法里初始化了 loginPresenter 对象,并绑定了自身作为 LoginView 的引用,并在登录按钮的点击事件中调用了 loginPresenter.login() 方法,从而将登录事件传递给了 loginPresenter。

LoginPresenterImpl

Presenter 就是 View 和 Model 之间的桥梁,我们来看看 LoginPresenterImpl 的关键代码:

public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {       private LoginView loginView;       private LoginModel loginModel;        public LoginPresenterImpl() {           this.loginModel = new LoginModelImpl();     }        @Override       public void attachView(LoginView loginView) {         this.loginView = loginView;     }        @Override     public void detachView() {         this.loginView = null;     }        @Override     public boolean isViewAttached(){         return loginView != null;     }        @Override     public void login(String username, String password) {           loginView.showLoading();           loginModel.login(username, password, this);     }        @Override     public void onLoginError(String msg) {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginError(msg);         }     }        @Override     public void onLoginSuccess() {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginSuccess();         }     } }

LoginModelImpl

最后,LoginModelImpl 就是对登录的业务逻辑处理了,一般是通过网络请求服务器实现登录逻辑,我们来看看伪代码:

public class LoginModelImpl implements LoginModel {       @Override       public void login(String username, String password, OnLoginFinishedListener listener) {           apiService.login(username, password)               .subscribeOn(Schedulers.newThread())               .observeOn(AndroidSchedulers.mainThread())               .subscribe(new Subscriber<Response>() {                     @Override                     public void onCompleted() {                     }                      @Override                     public void onError(Throwable e) {                           listener.onLoginError("Server Error")                     }                      @Override                     public void onNext(Response res) {                           if (res.isSuccess()) {                           listener.onLoginSuccess();                     } else {                           listener.onLoginError(res.getMsg());                     }                     }               });     } }

该示例代码用到了 Retrofit + RxJava,最核心的代码就在 onNext() 方法里,这是请求响应返回后被调用的方法。

至此,MVP 最简单的使用案例就讲解完了。另外,由于每一个业务功能基本都有对应的一套 MVP 的接口,因此,很多时候会将这一套接口封装在一起,组成一套契约,如下所示:

public interface LoginContract {       public interface LoginView {           ...     }        public interface LoginPresenter {           ...     }        public interface OnLoginFinishedListener {           ...     }        public interface LoginModel {           ...     } }

至此,对 MVP 最简单的用法就演示到这里结束。在实际应用中,应该做一些调整,比如抽离出一个 Base 层,将一些冗余的重复代码封装到对应的 Base 接口或类中,比如抽离出 BaseView、BaseActivity、BasePresenter、BaseListener 等。以及,设计上每一套 MVP 尽量设计成实现单一功能,以便能被复用,从而无需每一个页面都编写单独一套 MVP,尽量使用组合或继承的方式复用 MVP。

MVP 的优缺点

接着,我们来总结下 MVP 有哪些优缺点。先有看看有哪些优点呢:

  1. 我们知道,MVC 模式在 App 实际应用中,Activity 和 UIViewController 既同时担任 Controller 又担任部分 View 的职责,职责不清,导致 Activity 和 UIViewController 容易变得越来越臃肿。而应用 MVP 模式,直接将 Activity 和 UIViewController 划分到 View 层了,职责明确了,自然也避免了 Activity 和 UIViewController 臃肿的问题。
  2. MVP 之间的交互通过接口来进行的,那就便于进行单元测试了,维护性和扩展性也提高了。
  3. M 和 V 之间彻底分离了,降低了耦合性,修改 V 层也不会影响 M 层。

不过,相应地,相比 MVC 也引入了一些弊端:

  1. 由于增加了很多接口的定义,需要编写的代码量暴增,增加了项目的复杂度。
  2. 需要对很多业务模块之间的交互抽象成接口定义,对开发人员的设计能力要求更高了。

前一篇文章我们已经详细介绍了 MVC 模式的起源、通信机制和变种,以及在实际应用中的问题。如果对 MVC 理解还不透彻,建议先翻回去看前一篇文章。理解了 MVC 之后,MVP 和 MVVM 相对就容易理解了,下面我们就来深入理解这两个模式。

MVP

对 MVP 模式最早的解说来源于一篇 1996 年时发表的论文:《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》 。论文的作者是一个叫 Mike Potel 的人,当时是 Taligent 公司的 VP & CTO,Taligent 则是 IBM 的全资子公司。不过,如果你看过这篇论文的内容,就会发现,其实,最原始的 MVP 与我们现在所认识的 MVP 并不一样。论文中所论述的 MVP 其实是下面这样的结构:

025 | 正确认识MVP&amp;MVVM

该 MVP 其实是从数据管理用户界面两个维度的几个问题出发,将 Smalltalk 版本的 MVC 进行再分解演化而成,拆分出了几个中间组件:Interactor、Commands、Selections。Interactor 最好理解,其实就是 View 的交互事件。Commands 定义了对 Model 选定数据的一些操作,如删除、修改、保存等操作。Selections 可以理解为就是对 Model 数据集的筛选过滤,根据条件取子集。另外,所有的组件,也包括 Model、View、Presenter,都是通过接口进行交互的。

如果忽略掉三件套之间的几个中间组件,你就会发现,它们之间的依赖关系其实和 Smalltalk 版本的 MVC 是一样的。论文中也有提到,Presenter 其实就是 Controller,只是为了与 MVC 区别开来,所以才称为 Presenter

再来看看我们现在所熟知的 MVP 结构图:

025 | 正确认识MVP&amp;MVVM

看到这关系图,你还会发现,这和前篇文章说的变种 MVC 不是一模一样吗?没错,关系图的确是一样的,但背后的实现和角色划分却不太一样,我们后面就讲。至于,这种模式的 MVP 是如何演化而来的,我也不得而知,只知道这已经成为了当代 MVP 的标准结构。

在 MVP 里,三件套各自的职责和依赖关系和变种 MVC 里的职责和依赖关系其实是一样的,但不同的是,MVP 之间的交互主要是通过接口实现的,Model、View、Presenter 都有各自的接口,定义各自的行为方法。针对接口编程,自然就能减低耦合,提高可复用性,以及容易进行单元测试。

之前我们说过,实际应用中的 MVC,UIViewControllerActivity 其实是同时担任了 Controller 和部分 View 的角色的,职责划分不明确,才导致 UIViewController 和 Activity 的代码混乱不堪,越来越臃肿。而采用 MVP 模式,UIViewController 和 Activity 就明确地划分为 View 角色了,原有的 Controller 角色的职责则交由 Presenter 负责,职责清晰了,代码自然也容易清晰了。

MVP 的简单使用

我们就以一个简单的登录案例来说明如何使用 MVP,下图是该案例的类图:

025 | 正确认识MVP&amp;MVVM

定义了 4 个接口和 3 个实现类,其中,LoginActivity 是 Android 的 Activity 类,在 iOS 中,则可定义为 LoginViewController。LoginActivity 实现了 LoginView 接口,同时还会持有一个 LoginPresenter 对象。LoginView 和 LoginActivity 都明确划分到 View 层,LoginView 定义了登录流程中涉及到的几个UI层的接口方法,包括显示和隐藏加载框,以及登录失败时的错误信息展示,和登录成功后的处理,这些方法都会在 LoginPresenterImpl 的方法中被调用。Presenter 层定义了两个接口,LoginPresenter 只有一个登录的接口方法,OnLoginFinishedListener 则定义了登录结果的回调方法,两个接口统一由 LoginPresenterImpl 来实现即可。另外,从图中也可看到,LoginPresenterImpl 既持有一个 LoginView 对象,也持有一个 LoginModel 对象,LoginPresenterImpl 其实就是 LoginView 和 LoginModel 之间交互的桥梁。而 Model 层的 LoginModel 则不直接持有 LoginPresenter 对象,只在登录接口中加了一个回调对象的参数。

LoginActivity

下面,我们来简单看看一些关键代码的实现。先来看看 LoginActivity 的关键代码:

public class LoginActivity extends AppCompatActivity implements LoginView {     private LoginPresenter loginPresenter;       private ProgressBar progressBar;       private LoginButton loginBtn;       private EditText userNameEdt;       private EditText passwordEdt;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_login);           progressBar = findViewById(R.id.loading);           loginBtn = findViewById(R.id.login);           userNameEdt = findViewById(R.id.username);           passwordEdt = findViewById(R.id.password);          loginPresenter = new LoginPresenterImpl();           loginPresenter.attachView(this);            loginBtn.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 loginPresenter.login(userNameEdt.getText().toString(),                         passwordEdt.getText().toString());             }         });     }        @Override     public void showLoading() {         if (!progressBar.isShowing()) {             progressBar.show();         }     }      @Override     public void hideLoading() {         if (progressBar.isShowing()) {             progressBar.dismiss();         }     }      @Override     public void onLoginError(String msg) {         Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();     }        @Override     public void onLoginSuccess() {         Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();           startActivity(new Intent(this, MainActivity.class));     }        @Override     protected void onDestroy() {         super.onDestroy();         loginPresenter.detachView();     } }

代码逻辑比较简单,从代码中可知,在 onCreate() 方法里初始化了 loginPresenter 对象,并绑定了自身作为 LoginView 的引用,并在登录按钮的点击事件中调用了 loginPresenter.login() 方法,从而将登录事件传递给了 loginPresenter。

LoginPresenterImpl

Presenter 就是 View 和 Model 之间的桥梁,我们来看看 LoginPresenterImpl 的关键代码:

public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {       private LoginView loginView;       private LoginModel loginModel;        public LoginPresenterImpl() {           this.loginModel = new LoginModelImpl();     }        @Override       public void attachView(LoginView loginView) {         this.loginView = loginView;     }        @Override     public void detachView() {         this.loginView = null;     }        @Override     public boolean isViewAttached(){         return loginView != null;     }        @Override     public void login(String username, String password) {           loginView.showLoading();           loginModel.login(username, password, this);     }        @Override     public void onLoginError(String msg) {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginError(msg);         }     }        @Override     public void onLoginSuccess() {           if (isViewAttached()) {               loginView.hideLoading();                   loginView.onLoginSuccess();         }     } }

LoginModelImpl

最后,LoginModelImpl 就是对登录的业务逻辑处理了,一般是通过网络请求服务器实现登录逻辑,我们来看看伪代码:

public class LoginModelImpl implements LoginModel {       @Override       public void login(String username, String password, OnLoginFinishedListener listener) {           apiService.login(username, password)               .subscribeOn(Schedulers.newThread())               .observeOn(AndroidSchedulers.mainThread())               .subscribe(new Subscriber<Response>() {                     @Override                     public void onCompleted() {                     }                      @Override                     public void onError(Throwable e) {                           listener.onLoginError("Server Error")                     }                      @Override                     public void onNext(Response res) {                           if (res.isSuccess()) {                           listener.onLoginSuccess();                     } else {                           listener.onLoginError(res.getMsg());                     }                     }               });     } }

该示例代码用到了 Retrofit + RxJava,最核心的代码就在 onNext() 方法里,这是请求响应返回后被调用的方法。

至此,MVP 最简单的使用案例就讲解完了。另外,由于每一个业务功能基本都有对应的一套 MVP 的接口,因此,很多时候会将这一套接口封装在一起,组成一套契约,如下所示:

public interface LoginContract {       public interface LoginView {           ...     }        public interface LoginPresenter {           ...     }        public interface OnLoginFinishedListener {           ...     }        public interface LoginModel {           ...     } }

至此,对 MVP 最简单的用法就演示到这里结束。在实际应用中,应该做一些调整,比如抽离出一个 Base 层,将一些冗余的重复代码封装到对应的 Base 接口或类中,比如抽离出 BaseView、BaseActivity、BasePresenter、BaseListener 等。以及,设计上每一套 MVP 尽量设计成实现单一功能,以便能被复用,从而无需每一个页面都编写单独一套 MVP,尽量使用组合或继承的方式复用 MVP。

MVP 的优缺点

接着,我们来总结下 MVP 有哪些优缺点。先有看看有哪些优点呢:

  1. 我们知道,MVC 模式在 App 实际应用中,Activity 和 UIViewController 既同时担任 Controller 又担任部分 View 的职责,职责不清,导致 Activity 和 UIViewController 容易变得越来越臃肿。而应用 MVP 模式,直接将 Activity 和 UIViewController 划分到 View 层了,职责明确了,自然也避免了 Activity 和 UIViewController 臃肿的问题。
  2. MVP 之间的交互通过接口来进行的,那就便于进行单元测试了,维护性和扩展性也提高了。
  3. M 和 V 之间彻底分离了,降低了耦合性,修改 V 层也不会影响 M 层。

不过,相应地,相比 MVC 也引入了一些弊端:

  1. 由于增加了很多接口的定义,需要编写的代码量暴增,增加了项目的复杂度。
  2. 需要对很多业务模块之间的交互抽象成接口定义,对开发人员的设计能力要求更高了。

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 025 | 正确认识MVP&MVVM求职学习资料
分享到: 更多 (0)
D0b2wT.gif

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们