MVVM 模式介绍--摘自网友文章160402
2016-04-18 00:00
323 查看
原文地址:https://mp.weixin.qq.com/s?__biz=MzA3MjA4NjE3NQ==&mid=404502568&idx=1&sn=fe512f9820b99d3cc8212e363ff57195&scene=1&srcid=0407PIdwHgMcWOBI7P2zEgCa&key=b28b03434249256bd8382e5caf525bcb36d6b31e18f2e933d895b3f91b6a6e10a1d42d613b275625f39a121137975d80&ascene=0&uin=Mjc3OTU3Nzk1&devicetype=iMac+MacBookPro10%2C1+OSX+OSX+10.10.5+build%2814F27%29&version=11020201&pass_ticket=%2F8olw2Mg3%2BO3Hdredp8Koo4%2FdLP647sSn5SKAT3uD06Nfhyo6Peebt0qCKuYnh6O
什么是 MVVM 模式?
Model-View-ViewModel 就是将其中的 View 的状态和行为抽象化,让我们可以将 UI 和业务逻辑分开。当然这些工作 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
MVVM模式是通过以下三个核心组件组成,每个都有它自己独特的角色:
Model-包含了业务和验证逻辑的数据模型
View-定义屏幕中 View 的结构,布局和外观
ViewModel-扮演「View」和「Model」之间的使者,帮忙处理 View 的全部业务逻辑
那这和我们曾经用过的 MVC 模式有什么不同呢?以下是 MVC 的结构
View 在 Controller 的顶端,而 Model 在 Controller 的底部
Controller 需要同时关注 View 和 Model
View 只能知道 Model 的存在并且能在 Model 的值变更时收到通知
MVVM 模式和 MVC 有些类似,但有以下不同:
ViewModel 替换了 Controller,在 UI 层之下
ViewModel 向 View 暴露它所需要的数据和指令对象
ViewModel 接收来自 Model 的数据
你可以看到这两种模式有着相似的结构,但新加入的 ViewModel 是用不同的方法将组件们联系起来的,它是双向的,而 MVC 只能单向连接。
概括起来,MVVM 是由 MVC 发展而来-通过在 Model 之上而在 View 之下增加一个非视觉的组件将来自 Model 的数据映射到 View 中。接下来,我们将更多地看到 MVVM 的这种特性。
THE HACKER NEWS READER
正如前面提及过的,我将我原来的一个项目拆开为这篇文章服务。这款应用有以下几种特性:
查看帖子列表
查看单个帖子
查看帖子下的评论
查看指定作者的帖子
下面的图片能让你很快了解它是怎么工作的:
左边的图片展示的是帖子的列表,它也是这款应用的主要部分,接下来右边的图片展示的是该帖子的评论列表,它和前者有相似的地方,但也有一些不同,我们将在后面看到。
展示帖子
每个帖子信息都用 RecyclerView 所包含的 CardView 包装起来,正如上图展示的。
使用 MVVM 我们可以将不同层抽象出来很好的实现这些卡片,这意味着每个 MVVM 组件只要处理它被分配的任务即可。通过使用前面介绍的 MVVM 的不同组件,组合在一起后能构造出我们的帖子卡片实例,那么我们该如何将它们从布局中抽离出来?
MODEL
简单来说,Model 由那些帖子的业务逻辑组成,包括一些像 id,name,text 之类的属性,以下代码展示了该类的部分代码:
为了可读性,上面的 POST 类中去掉了一些 Parcelable 变量和方法
这里你可以看到 Post 类只包含所有它的属性,没有一点别的逻辑 - 别的组件会处理它们。
View
View 的任务是定义布局,外观和结构。View 最好能完全通过 XML 来定义,即使它包含些许 Java 代码也不应该有业务逻辑部分,
View 会通过绑定从 ViewModel 中取出数据。在运行时,若 ViewModel 的属性的值有变化的话它会通知 View 来更新UI。
首先,我们先给 RecyclerView 传入一个自定义的适配器。为此,我们需要让我们的 BindingHolder 类持有对 Binding 的引用。
onBindViewHolder() 方法才是真正将 ViewModel 和 View 绑定的地方。我们获取一个 ItemPostBinding 对象(它会被 item_post 布局自动生成),然后将新建的 PostViewModel 对象传给它的 ViewModel 引用。
下面就是完整的 PostAdaper 类:
看下我们的XML布局,首先我们要将所有的布局都包含在layout标签下,同时使用data标签来声明我们的 ViewModel:
声明 ViewModel 可以让我们在整个布局中引用它,在 item_post 布局中我们会多次用到 ViewModel:
androidText-你可以从 ViewModel 中引用相应的方法给文本视图设置内容。正如下面你所看到的 @{viewModel.postTitle},它从 ViewModel 中引用了 getPostTitle() 方法-它将返回相应帖子的标题。
onClick-我们也可以引用单击事件到布局文件中。如你所看到的,@{viewModel.onClickPost} 是指从 ViewModel 中引用 onClickPost() 方法-它将返回一个能处理单击事件的 OnClickListener 对象。
visibility - 控制去 comments activity 的入口,依赖于该帖子是否有相应的评论。通过检查 comments list 的长度来决定该 visibility 的值,这些操作都是在 ViewModel 中完成的。在这里,我们引用了它的 getCommentsVisiblity() 方法来计算是否该显示
这样做实在太棒了,我们能抽象出显示逻辑到我们的布局文件中,让我们的 ViewModel 来关注它们。
VIEWMODEL
ViewModel 扮演了 View 和 Model 之间使者的角色,让它来关注所有涉及到 View 的业务逻辑,同时它可以访问 Model 的方法和属性,这些最终会作用到 View 中。通过 ViewModel,可以移除原本需要在别的组件中返回或处理的数据。
在这里,PostViewModel 用 Post 对象来处理 CardView 需要显示的内容,在下面的类中,你可以看到一系列的方法,每个方法对最终作用于我们的帖子视图。
getPostTitle()-通过 Post 对象返回一个帖子的标题
getPostAuthor()-这个方法首先会从应用的resources中获取相应的字符串,然后传入Post对象的author属性对它进行格式化,如果isUserPosts 等于true我们就需要加入下划线,最终返回该字符串。
getCommentsVisibility()-该方法决定是否显示有关评论的TextView onClickPost()-该方法返回相应View需要的OnClickListener
这些例子表明不同的业务逻辑都有我们的 ViewModel 来处理。下面就是我们 PostViewModel 类的完整代码以及那些被item_post布局引用的方法。
是不是很爽?正如你看到的,我们的 PostViewModel 关注以下方面:
维护 Post 对象的属性,最终会在 View 中展示
对这些属性进行相应的格式化
通过 onclick 属性给相应的views对提供点击事件的支持
通过 Post 对象的属性处理相关 views 的显示
测试 VIEWMODEL
使用 MVVM 的一大好处是我们可以很容易对 ViewModel 进行单元测试。在 PostViewModel 中,可以写些简单的测试方法来验证我们的 ViewModel 是否正确实现。
shouldGetPostScore()-测试getPostScore()方法,确认该帖子的得分是否正确地格式化成字符串对象并返回。
shouldGetPostTitle()-测试getPostTitle()方法,确认该帖子的标题被正确返回。
shouldGetPostAuthor()-测试getPostAuthor()方法,确认返回的帖子的作者被正确地格式化了
shouldGetCommentsVisiblity()-测试getCommentsVisibility()方法是否正确返回了visibility属性的值,它将会用在帖子的 Comments 按钮中。我们传入一个包含不同状态的ArrayLists来确认它是否能正确返回。
现在我们可以知道的 ViewModel 已经正确工作了!!
评论
实现评论的方法和前面很像但还是有点不同。
有两个不同的 ViewModel 被用来操作这次评论, CommentHeaderViewModel 和 CommentViewModel。正如你在CommentAdapter中看到的,我们的 View 有两种的不同类型:
private static final int VIEW_TYPE_COMMENT = 0;
private static final int VIEW_TYPE_HEADER = 1;
如果该帖子是一个发问的帖子,我们将在屏幕的顶端显示一个头部,它显示所问的问题-接着评论会正常显示在下面。同时你应该会注意到在 onCreateViewHolder() 中我们会通过判断 VIEW_TYPE 来加载不同的布局,它会返回两种不同布局中的其中一种。
接着在我们的 onBindViewHolder() 方法中我们会根据不同的视图类型来创建绑定。这是因为不同的 ViewModel 对头部有不同的处理方法
这就是它们的不同点,评论部分有两个不同的 ViewModel 类型 — 取决于该帖子是否是发问类的帖子。
总结
如果正确使用,数据绑定类库可能会改变我们开发应用的方式。当然,还有其他方法实现数据的绑定,使用 MVVM 模式只是其中的一种途径。
比如,你可以在布局中引用我们的 Model 然后通过它的变量引用直接访问它的属性:
同时我们可以很容易从adapers和classes中移除一些基础的显示逻辑。下面有种很新颖的方法实现我们这种需求:
这就是我看到上面实现方式的表情!
我认为这是数据绑定类库中不好的地方,它将 View 的显示逻辑包含到了 View 中。不仅会造成混乱,也让我们的测试和调试变的更加困难,因为它将逻辑和布局混淆在一起。
当然,认定MVVM是开发应用的正确方式还为时过早,但这次尝试也让我有机会见识到未来项目的一种趋势。如果你想阅读更多有关数据绑定类库的文章,你可以看这里。同时微软也有一篇关于MVVM通俗易懂的文章。
我很愿意听取你们想法,如果你们有任何的看法和建议可以随时发 Tweet 和我讨论!
什么是 MVVM 模式?
Model-View-ViewModel 就是将其中的 View 的状态和行为抽象化,让我们可以将 UI 和业务逻辑分开。当然这些工作 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
MVVM模式是通过以下三个核心组件组成,每个都有它自己独特的角色:
Model-包含了业务和验证逻辑的数据模型
View-定义屏幕中 View 的结构,布局和外观
ViewModel-扮演「View」和「Model」之间的使者,帮忙处理 View 的全部业务逻辑
那这和我们曾经用过的 MVC 模式有什么不同呢?以下是 MVC 的结构
View 在 Controller 的顶端,而 Model 在 Controller 的底部
Controller 需要同时关注 View 和 Model
View 只能知道 Model 的存在并且能在 Model 的值变更时收到通知
MVVM 模式和 MVC 有些类似,但有以下不同:
ViewModel 替换了 Controller,在 UI 层之下
ViewModel 向 View 暴露它所需要的数据和指令对象
ViewModel 接收来自 Model 的数据
你可以看到这两种模式有着相似的结构,但新加入的 ViewModel 是用不同的方法将组件们联系起来的,它是双向的,而 MVC 只能单向连接。
概括起来,MVVM 是由 MVC 发展而来-通过在 Model 之上而在 View 之下增加一个非视觉的组件将来自 Model 的数据映射到 View 中。接下来,我们将更多地看到 MVVM 的这种特性。
THE HACKER NEWS READER
正如前面提及过的,我将我原来的一个项目拆开为这篇文章服务。这款应用有以下几种特性:
查看帖子列表
查看单个帖子
查看帖子下的评论
查看指定作者的帖子
下面的图片能让你很快了解它是怎么工作的:
左边的图片展示的是帖子的列表,它也是这款应用的主要部分,接下来右边的图片展示的是该帖子的评论列表,它和前者有相似的地方,但也有一些不同,我们将在后面看到。
展示帖子
每个帖子信息都用 RecyclerView 所包含的 CardView 包装起来,正如上图展示的。
使用 MVVM 我们可以将不同层抽象出来很好的实现这些卡片,这意味着每个 MVVM 组件只要处理它被分配的任务即可。通过使用前面介绍的 MVVM 的不同组件,组合在一起后能构造出我们的帖子卡片实例,那么我们该如何将它们从布局中抽离出来?
MODEL
简单来说,Model 由那些帖子的业务逻辑组成,包括一些像 id,name,text 之类的属性,以下代码展示了该类的部分代码:
为了可读性,上面的 POST 类中去掉了一些 Parcelable 变量和方法
这里你可以看到 Post 类只包含所有它的属性,没有一点别的逻辑 - 别的组件会处理它们。
View
View 的任务是定义布局,外观和结构。View 最好能完全通过 XML 来定义,即使它包含些许 Java 代码也不应该有业务逻辑部分,
View 会通过绑定从 ViewModel 中取出数据。在运行时,若 ViewModel 的属性的值有变化的话它会通知 View 来更新UI。
首先,我们先给 RecyclerView 传入一个自定义的适配器。为此,我们需要让我们的 BindingHolder 类持有对 Binding 的引用。
onBindViewHolder() 方法才是真正将 ViewModel 和 View 绑定的地方。我们获取一个 ItemPostBinding 对象(它会被 item_post 布局自动生成),然后将新建的 PostViewModel 对象传给它的 ViewModel 引用。
下面就是完整的 PostAdaper 类:
看下我们的XML布局,首先我们要将所有的布局都包含在layout标签下,同时使用data标签来声明我们的 ViewModel:
声明 ViewModel 可以让我们在整个布局中引用它,在 item_post 布局中我们会多次用到 ViewModel:
androidText-你可以从 ViewModel 中引用相应的方法给文本视图设置内容。正如下面你所看到的 @{viewModel.postTitle},它从 ViewModel 中引用了 getPostTitle() 方法-它将返回相应帖子的标题。
onClick-我们也可以引用单击事件到布局文件中。如你所看到的,@{viewModel.onClickPost} 是指从 ViewModel 中引用 onClickPost() 方法-它将返回一个能处理单击事件的 OnClickListener 对象。
visibility - 控制去 comments activity 的入口,依赖于该帖子是否有相应的评论。通过检查 comments list 的长度来决定该 visibility 的值,这些操作都是在 ViewModel 中完成的。在这里,我们引用了它的 getCommentsVisiblity() 方法来计算是否该显示
这样做实在太棒了,我们能抽象出显示逻辑到我们的布局文件中,让我们的 ViewModel 来关注它们。
VIEWMODEL
ViewModel 扮演了 View 和 Model 之间使者的角色,让它来关注所有涉及到 View 的业务逻辑,同时它可以访问 Model 的方法和属性,这些最终会作用到 View 中。通过 ViewModel,可以移除原本需要在别的组件中返回或处理的数据。
在这里,PostViewModel 用 Post 对象来处理 CardView 需要显示的内容,在下面的类中,你可以看到一系列的方法,每个方法对最终作用于我们的帖子视图。
getPostTitle()-通过 Post 对象返回一个帖子的标题
getPostAuthor()-这个方法首先会从应用的resources中获取相应的字符串,然后传入Post对象的author属性对它进行格式化,如果isUserPosts 等于true我们就需要加入下划线,最终返回该字符串。
getCommentsVisibility()-该方法决定是否显示有关评论的TextView onClickPost()-该方法返回相应View需要的OnClickListener
这些例子表明不同的业务逻辑都有我们的 ViewModel 来处理。下面就是我们 PostViewModel 类的完整代码以及那些被item_post布局引用的方法。
是不是很爽?正如你看到的,我们的 PostViewModel 关注以下方面:
维护 Post 对象的属性,最终会在 View 中展示
对这些属性进行相应的格式化
通过 onclick 属性给相应的views对提供点击事件的支持
通过 Post 对象的属性处理相关 views 的显示
测试 VIEWMODEL
使用 MVVM 的一大好处是我们可以很容易对 ViewModel 进行单元测试。在 PostViewModel 中,可以写些简单的测试方法来验证我们的 ViewModel 是否正确实现。
shouldGetPostScore()-测试getPostScore()方法,确认该帖子的得分是否正确地格式化成字符串对象并返回。
shouldGetPostTitle()-测试getPostTitle()方法,确认该帖子的标题被正确返回。
shouldGetPostAuthor()-测试getPostAuthor()方法,确认返回的帖子的作者被正确地格式化了
shouldGetCommentsVisiblity()-测试getCommentsVisibility()方法是否正确返回了visibility属性的值,它将会用在帖子的 Comments 按钮中。我们传入一个包含不同状态的ArrayLists来确认它是否能正确返回。
现在我们可以知道的 ViewModel 已经正确工作了!!
评论
实现评论的方法和前面很像但还是有点不同。
有两个不同的 ViewModel 被用来操作这次评论, CommentHeaderViewModel 和 CommentViewModel。正如你在CommentAdapter中看到的,我们的 View 有两种的不同类型:
private static final int VIEW_TYPE_COMMENT = 0;
private static final int VIEW_TYPE_HEADER = 1;
如果该帖子是一个发问的帖子,我们将在屏幕的顶端显示一个头部,它显示所问的问题-接着评论会正常显示在下面。同时你应该会注意到在 onCreateViewHolder() 中我们会通过判断 VIEW_TYPE 来加载不同的布局,它会返回两种不同布局中的其中一种。
接着在我们的 onBindViewHolder() 方法中我们会根据不同的视图类型来创建绑定。这是因为不同的 ViewModel 对头部有不同的处理方法
这就是它们的不同点,评论部分有两个不同的 ViewModel 类型 — 取决于该帖子是否是发问类的帖子。
总结
如果正确使用,数据绑定类库可能会改变我们开发应用的方式。当然,还有其他方法实现数据的绑定,使用 MVVM 模式只是其中的一种途径。
比如,你可以在布局中引用我们的 Model 然后通过它的变量引用直接访问它的属性:
同时我们可以很容易从adapers和classes中移除一些基础的显示逻辑。下面有种很新颖的方法实现我们这种需求:
这就是我看到上面实现方式的表情!
我认为这是数据绑定类库中不好的地方,它将 View 的显示逻辑包含到了 View 中。不仅会造成混乱,也让我们的测试和调试变的更加困难,因为它将逻辑和布局混淆在一起。
当然,认定MVVM是开发应用的正确方式还为时过早,但这次尝试也让我有机会见识到未来项目的一种趋势。如果你想阅读更多有关数据绑定类库的文章,你可以看这里。同时微软也有一篇关于MVVM通俗易懂的文章。
我很愿意听取你们想法,如果你们有任何的看法和建议可以随时发 Tweet 和我讨论!
相关文章推荐
- Kotlin中局部方法的深入探究
- Kotlin object express 匿名内部类
- Kotlin
- Kotlin 学习(1) -- Hello World
- 学习kotlin 记录笔记。----hello word
- Kotlin的中文文档
- 学习Kotlin笔记记录。--------控制流
- 学习Kotlin笔记记录。--------类
- Kotlin For Android初体验——配置Kotlin
- Kotlin For Android简介——有趣实用的功能
- android studio 配置kotlin 环境
- 闲话js前端框架(4)——组件化?有没有后端的事?
- Kotlin在Android工程中的应用
- 使用Kotlin开发Android应用(II):创建新工程
- 使用Kotlin开发Android应用(III):扩展函数和默认值
- 使用Kotlin开发Android应用(IV):自定义视图和Android扩展
- Kotlin基础
- 重读《Kotlin For Android》二
- Kotlin学习教程
- 在Kotlin的语言环境下使用ButterKnife框架