写点什么

关于 MVC_MVP_MVVM 的一些错误认识,android 面试流程

用户头像
Android架构
关注
发布于: 10 小时前

注:这篇文章里我将使用 MVX 做为 MVC 、MVP 以及 MVVM 的统称。


我们都知道 MVX 的进化过程是从滚球兽进化到 MVC ,然后从 MVC 进化到 MVP,再从 MVP 超进化到 MVVM。那么接下来,按照常规的套路,我应该要介绍什么是 MVC,什么是 MVP,以及什么是 MVVM,并且分别介绍 M、V、C/P/VM 各自的职责了。


达嘎,口头瓦鲁!



我的目的是想要纠正一些对 MVX 的错误认识,所以前提是你要对 MVX 有一些了解。为了避免有人在使用 MVX 时走上弯路,所以决定对我看到的一些关于 MVX 的错误认识进行总结以及纠正。会产生这些错误认知的原因,经我分析,其实是:没有真正领会到 MVX 主义的核心价值观!其实 MVX 的核心思想也很简单,不要误会,不是富强、民主、……而是 将表现层和业务层分离

表现层和业务层分离

表现层和业务层分离,Matin Fowler 称之为 [Separated Presentation](


)。这里的表现层就是 VX,业务层就是 M。如果有人看到这里发现了和你认为的 MVX 不一样的话,那么你对 MVX 的认识很可能就存在错误,严重者还可能是走了修正主义路线!


从表现层和业务层分离的视角来看,M、V、X 不是平等的身份,应该是 M 和 V-X。自始自终 M 的职责都没变,变的是 V-X,随着软件开发技术的发展、交互形式或者交互媒介的不断改变,表现层的逻辑也越来复杂,MVX 的进化过程就是一个不断探寻处理表现层复杂逻辑的过程。当然从一个形态进化到另一个形态,并不一定是为了解决更复杂的交互逻辑,也可能是有了一种“更优雅”的方式来处理表现层逻辑。


既然已经有表现层和业务层分离的概念了,那么第一个错误观点就很好解释了。

错误一:Presenter 或者 ViewModel 负责处理业务逻辑

这是一个很常见的错误观点,很多介绍 MVP 或者 MVVM 的文章都这么说过。正如前面所说,业务逻辑是属于 M 层的,那 Presenter 或者 ViewModel 是干什么的,处理表现层逻辑的吗?是的,或者说大部分表现层逻辑都是在 Presenter 或者 ViewModel 中处理的。之前我将业务层之上的这些逻辑称之为视图逻辑,现在为了统一就叫做表现层逻辑吧(加个吧字怎么感觉怪怪的)。


我在这里就简单说一下什么是表现层逻辑,以及 View 和 Presenter/ViewModel 又是如何分工的。假设你的应用有一个个人资料的 profile 页面,这个页面有两种状态,一种是浏览状态,一种是编辑状态,通过一个编辑按钮触发状态的转换,编辑状态时,部分信息项可以进行编辑。那这里就有一个明显的表现层逻辑,那就是点击按钮切换浏览/编辑状态。


现在的 MVP 的流行形态(或者变种)叫做 [Passive View](


),它和 MVVM 一样现在都倾向于将几乎所有的表现层逻辑交给 Presenter 或者 ViewModel 处理,View 层需要做的事情很少,基本上就是接受用户事件,然后将用户事件传递给 Presenter 或者 ViewModel。以上面的 prof


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ile 页面的例子来解释的话就是,View 层负责接收编辑按钮的点击事件,然后通知 Presenter/ViewModel,然后 Presenter/ViewModel 通知 View 是显示浏览状态的视图还是编辑状态的视图。MVP 的示例代码大概是这样的:


public class ProfileView {


void initView() {// 负责注册点击事件监听器,并将点击事件通知给 presentereditStateButton.setOnClickListener(new OnClickListener() {presenter.onEditStateButtonClicked();})...}


// 显示浏览状态视图,想不到好名字,就叫 showNormalState 吧 public void showNormalState() {// 浏览状态下编辑按钮提示文字为“编辑”,所有项不可编辑 editStateButton.setText("编辑");nickName.setEditable(false);...}


public void showEditState() {// 浏览状态下编辑按钮提示文字为“完成”,部分项要设置为可编辑 editStateButton.setText("完成");nickName.setEditable(true);...}}


public class ProfilePresenter {


private State curState = State.NORMAL;


public void onEditStateButtonClicked() {// 按钮被点击时,根据当前状态判断 View 应该切换显示的状态// 这就是表现层逻辑 if (isInEditState()) {curState = State.NORMAL;view.showNormalState();} else {curState = State.EDIT;view.showEditState();}}


private boolean isInEditState() {return curState == State.EDIT;}


@VisibleForTestvoid setState(State state) {curState = state;}}


注:这个示例代码只是为了展示表现层逻辑,没有涉及到 Model 层,编译也不会通过的!


能感受到我想表达的意思吗?就是 Presenter/ViewModel 根据当前交互状态决定该显示什么,而 View 要做的是如何显示它们。再比如说下拉刷新的场景,由 View 告诉 Presenter/ViewModel,它接收到了下拉事件,然后 Presenter/ViewModel 再告诉 View,让它去显示刷新提示视图,至于这个刷新提示长什么样就由 View 来决定。当然 Presenter/ViewModel 也可能会判断当前网络不可用,而让 View 显示一个网络不可用的提示视图。


为什么要让 Presenter/ViewModel 处理几乎所有的表现层逻辑呢?主要是为了提高可测试性,将尽可能多的表现层逻辑纳入到单元测试的范围内。因为对视图控件的显示等等进行单元测试太难了,所以 View 是基本上没法进行单元测试的,但是 Presenter/ViewModel 是完全可以进行单元测试的:


public class ProfilePresenterTest {


private ProfilePresenter presenter;private ProfileView view;


@Testpublic void testShowEditStateOnButtonClick() {// 浏览状态下点击编辑按钮,验证 View 是否显示了编辑状态视图// 也就是验证 view.showEditState()方法是否被调用了 presenter.setState(State.NORMAL);presenter.onEditStateButtonClicked();Mockito.verify(view).showEditState();}


@Testpublic void testShowNormalStateOnButtonClick() {// 编辑状态下点击完成按钮,验证 View 是否显示了浏览状态视图// 也就是验证 view.showNormalState()方法是否被调用了 presenter.setState(State.EDIT);presenter.onEditStateButtonClicked();Mockito.verify(view).showNormalState();}}


你看,这些表现层逻辑就都能进行单元测试了吧!大概懂我意思了吧?



OK,现在你已经知道表现层了,那业务层又是干什么用的呢?现在我们就要开始谈到 M 了。


M 是什么?M 是指那些喜欢从受虐中获得性……哎呀,不好意思,搞混了!哎~学识渊博就是麻烦!M 者,Model 也,再长一点就是 Domain Model,中文名字叫领域模型。我们看一下维基百科上对 [Domain model](


) 的定义:


In software engineering, a domain model is a conceptual model of the domain that incorporates both behaviour and data.


怎么样,是不是很通俗易懂呀?当然不是!刚刚开始有点理解 Model 层是处理业务逻辑的,现在又来了个抖 MMM…… Domain,我都不知道该往哪里去想了!Domain,简单点就把它理解成业务,我觉得都没啥问题。我这里引用这句话,主要是想强调,Model 层包含了业务数据以及对业务数据的操作 (behaviour and data),也是为了引出第二个错误观点。

错误二:Model 就是静态的业务数据

我们做业务模块开发时,会经常定义一些数据结构类,比如个人资料可能会对应一个 UserProfile 类,一条订单数据可能会对应一个 Order 类,这些类没有任何逻辑,只有一些简单的 getter、setter 方法。有些人会认为像 UserProfile 或者 Order 这样的数据结构类就是 Model。


我们已经强调了,Model 层包含了业务数据以及对业务数据的操作。像 UserProfile 或者 Order 这样的数据结构类的实例甚至都不能称之为对象,可以看一下 Uncle Bob 的 [Classes vs. Data Structures](


) 这篇文章,对象是有行为的,一个数据结构实例没有行为,连对象都称不上,怎么能代表 Model 层呢!


静态的业务数据不能代表 Model 层,业务数据以及针对业务数据的操作共同构成了 Model 层,这也就是业务逻辑。再举个例子说一下吧,假设你在做一个叫“掘铁”的 app,这个 app 现在只有一个页面,用来展示推荐的博客列表。OK,我们如果用 MVP 的形式该怎么写呢?我们就先不管和 Model 层完全没有交互的 View 了,Presenter 层除了处理表现层逻辑外,还要向 Model 层发出业务指令,注意,Presenter 并不处理业务逻辑,真正的业务逻辑还是由 Model 层完成。示例代码大概是下面这样:


public class RecommendBlogFeedPresenter {


private RecommendBlogFeedView view;private BlogMode model;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
关于MVC_MVP_MVVM的一些错误认识,android面试流程