资讯专栏INFORMATION COLUMN

[译注] MVVM 模式

banana_pi / 2662人阅读

摘要:由于控件与业务逻辑之间的紧耦合,相应带来的问题就是变更的代价增大,以及难以编写针对性的单元测试。这些东西的组合提供了到译者注视图模型后面统一简称之间的连接方式。的单元测试可以完全模拟在上用的那些功能。

原文:https://github.com/kuitos/kui...
全部文章:https://github.com/kuitos/kui...

[译注] MVVM 模式

原文:The MVVM Pattern

MVVM 模式跟 Silverlight 这类 XAML 应用平台是天生合拍的。这是因为 MVVM 模式利用了Silverlight 的一些特殊能力,比如说 数据绑定,命令,行为等。MVVM 跟其他一些将表现及UI布局 与展示层逻辑的职责进行分离的模式很相似;如果你对 MVC 模式熟悉的话,你会发现它与 MVVM 之间存在很多相似的概念。

译者注:XAML(Extensible Application Markup Language)是微软为构建GUI程序而创建的一种标记语言,你可以将它等同于 web 体系中 HTML。以下译文中,web 开发者可以将 XAML 统一代入为 HTML。Silverlight 则是微软开发的一个 富互联网应用(Rich Internet Application) 开发平台。

MVVM 模式的设计动机

诸如 Windows Forms,WPF,Silverlight 以及 Windows Phone 这类开发技术都提供了一个默认的体验,那就是它可以让开发者从工具箱中拖拽控件到设计面板,然后在代码文件中编写一定格式的代码就能完成整个开发。但是随着这类应用的增长规模及作用范围的变化,复杂的维护性问题就会随之而来。由于 UI控件 与 业务逻辑 之间的紧耦合,相应带来的问题就是 UI 变更的代价增大,以及难以编写针对性的单元测试。

使用 MVVM 模式来实现应用的主要动机有以下几点:

它提供了一系列分离的概念。紧耦合,难变化,脆弱的代码导致了各种各样的长期维护的问题,最后导致了客户对交付的软件较低的满意度。在应用逻辑及 UI 之间进行干净的分离将会让应用更容易测试,维护以及拓展。它提高了代码的重用性同时使得 开发-设计 的工作流变为可能。

它与 XAML 平台是天生合拍的。MVVM 模式的关键推动力来自于 Silverlight 平台丰富的数据绑定技术栈及一些依赖属性。这些东西的组合提供了 UI 到 VM(译者注:view model 视图模型后面统一简称 VM) 之间的连接方式。

它使得 开发-设计 的工作流成为可能。当 UI XAML 不是与代码紧耦合时,设计师们就很容易去自由的发挥他们的创造力,从而做出一个更优秀的产品。(译者:简单来说就是 视图层 跟 M/VM 层的开发可以是正交的。)

它增强了应用的可测试性。将 UI 逻辑转移到一个可以独立实例化的类中,可以让单元测试的编写更加容易。(译者:这一点其实非常重要)

MVVM 模式

Model-View-ViewModel 的模式可以用到所有的 XAML 平台上。它的意图是在 UI 控件和它们的逻辑之间进行一个纯净的概念分离。

MVVM 模式中有三个核心的组件:model(模型),view(视图) 以及 view-model(视图模型)(译者注:为保证交流中术语的一致性,model、view、view-model后面均不作翻译),它们彼此间扮演一个截然不同的角色。下面的插图展示了这三个组件之间的关系:

每个组件之间都是相互解耦的,这也使得:

组件可以被交换

内部实现可以在不影响其他组件的情况下修改

组件可以独立的运作

隔离的单元测试

除了理解这三个组件的职责,理解组件之间是如何交互的也同样重要。在最高的层级上,view 能感知到 view-model,view-model 能感知到 model,但是 model 并不会察觉到 view-model 的存在,同样的,view-model 也察觉不到 view。

view-model 将 model 类 与 view 隔离开来,这也使得 model 可以独立于 view 进行演化。

View

view 的职责是用来定义 结构、布局,及用户在屏幕上看到的外观表现。理想情况下,view 仅通过 XAML 定义,以及一些有限的不包含业务逻辑的代码。

在一个 Windows Phone 应用中,view 通常是一个页面。除此之外,一个 view 也可以是一个父 view 的子组件,或者一个 ItemsControl 中的 DataTemplate对象。

一个 view 可以有自己的 view-model,或者继承自父级 view 的 view-model。视图通过绑定,或者调用 view-model 上的方法来获取数据。在运行期间,UI 控件将会响应 view-model 属性触发的变化通知,从而改变 view。

有一些 view 上的交互会触发 view-model 上的代码执行,比如说按钮点击或者选项选中事件。如果这个控件是一个命令源,这个控件的 Command 属性可以在 view-model 上绑定成一个 ICommand 属性。当这个控件的命令被调用,view-model 上相应的代码就会被执行。除了命令,行为也能被附加到 view 的一个对象中,然后监听命令调用及事件触发。作为回应,这个行为之后可以调用 view-mode 上的 ICommand 或者方法。(译者:web 领域里这些就是指的模板上的事件绑定语法,通常由框架提供)

Model

model 在 MVVM 中是应用的域模型(domain model)实现,它包含数据模型以及相应的业务和校验逻辑。model 对象的例子包括,数据仓库(repositories),业务对象,数据转换对象(DTOs),POCO对象,以及生成的实体及代理对象。

译者:一个 model 应该包含基本的模型数据、基本的业务逻辑、业务规则、数据转换、依赖校验逻辑等。这里要提到的一个概念就是,域模型分为两个类型,一类是贫血的(Anemic domain model),一类是非贫血的(non-anemic)。贫血的域模型只包含基础的数据信息,而不含有其他数据校验、业务规则等逻辑,它更像是一种数据库结构在代码层面的还原。在贫血域模型设计中,业务逻辑通常作为一个独立的代码部分,用来转换模型对象的状态,且各个处理之间可以组合嵌套(用过Redux的同学有没有觉得很熟悉的感觉?)。在面向对象设计领域,这通常被视为一个反模式。(OO与FP之间的冲突)

View Model

view-mode 作为 view 和 model 的中间人,它的职责是用于处理 view 的逻辑。通常情况下,view-model 与 model 之间的交互是通过调用 model 类中的方法来完成的。之后 view-model 依据 model 中的数据提供一种方便 view 使用的格式。view-model 从 model 中获取数据然后使其对 view 可用的同时,为了让 view 操作起来更简单,可能会通过一些方式做数据格式转换。view-model 还提供了一些命令的实现让应用的用户可以在 view 中使用。比如说,当用户点击了 UI 中的一个 button,这个动作可以触发 view-model 中的一个命令。view-model 同样有职责去定义一些某些方面会影响 view 展示的逻辑状态的变化,比如说一个表明一些操作是挂起的状态。

为了让 view-model 参与到与 view 的双向数据绑定当中,它的属性必须触发 PropertyChanged 事件。

(译者注:后面这两段是基于 .NET 平台的具体代码实践,换算到 web 领域,基本上说的就是在 VM 中监听数据变化,然后做出对应的反应。)

View-model 通过实现 INotifyPropertyChanged 接口以及属性变化时触发的 PropertyChanged 事件来满足这个需求。当属性发生变更时,监听者可以适当做回应。

对于集合而言,相应的提供了视图友好的工具System.Collections.ObjectModel.ObservableCollection 。这个工具实现了集合变更通知,从而减轻了开发者自己在集合上实现 INotifyCollectionChanged 接口的负担。

译者:这里提到了一个很重要的事情,就是 vm 获取到 model 中的数据后,可能需要做一些相应的数据格式化,以方便 view 使用。在 ng1.x 及 vue1.x 中,这类操作通常是通过在视图模板上绑定过滤器语法实现的(ng-bind="model | upperCase"),然而 MVVM 定义中,数据转换/格式化 的操作本身也是属于 VM 的一部分。这也是为什么我非常赞同 vue2 中废除 filter 的一部分原因:Vue 2.0 - Bring back filters please

连接 View Models 到 Views

MVVM 利用了 Silverlight 中的数据绑定能力以及行为和事件触发器来管理 view 和 view-model 之间的联系。这些能力将业务代码需要出现在视图代码中必要性变得很低。

有很多用来连接 view-model 和 view 的方法,包括直接的关系以及基于容器的方式。然而,所有的方式共有的一个目标就是,给 view 的 DataContext 属性分配一个 view-model。

Views 可以与 view models 在独立代码文件里建立连接,也可以直接在 view 中。

译者:这里提到了 silverlight 里的 DataContext,换到 web 领域的 MVVM 框架里我们可以理解成作用域,即框架通过给某个视图的作用域分配一个 view-model 的方式,来完成 view 和 view-model 的衔接。

Code-Behind(独立代码)

一个 view 的代码可以是在独立代码文件中,于此同时 view-model 需要分配成它的 DataContext 属性。这可以是通过一个简单的 view 初始化一个新的 view-model 然后分配给它的 DataContext 属性来完成,也可以通过 view 使用控制反转(IOC)容器注入一个 view-model 来实现。

但是,在独立代码中 连接一个 view-model 到 view 中的做法是不推荐的,它可能给同时使用 VS 及 MSB(Microsoft Expression Blend®) 设计软件的设计师造成问题。

View

如果一个 view-model 没有任何的构造器参数,它可以被当做 view 的 DataContext 来实例化。一个通常的实现方法是使用一个 view-model 定位器。这个资源可以公开应用的 view models 作为属性,从而使得独立的 view 可以绑定上去。这种方式意味着应用只有一个类用来连接 view models 到 views。此外,它仍然让开发者可以自由选择手动执行 view-model 定位器内的连接,或者使用一个依赖注入容器。

译者:框架实现指导

MVVM 的优势

MVVM 使得完美的 开发-设计 工作流变为可能,它具备以下优点:

开发期间,工程师和设计师可以更独立并行的在各自的组件上工作。设计师可以专注于 view,如果他们使用 Expression Blend,他们可以轻松的生成示例数据,而这个时候开发者只需要专注于 view model 和 model 组件。

开发者可以为 view-model 和 model 编写单元测试,而不用去管 view。view-model 的单元测试可以完全模拟在 view 上用的那些功能。

由于 view 是完全由 XAML 实现的,这也让应用的 UI 重新设计变得简单,而且还不用去触碰到逻辑代码。一个新的版本的 view 应该依然可以与之前已存在的 view-model 一起运行。

如果已经存在一个封装好了已有业务逻辑的 model ,改这个 model 可能会比较困难或有风险。在这个场景中,view-model 应该作为一个 model 类的适配器,从而避免 model 的代码需要做大的变动。(译者注:也就是说,view-model 应该对 model 中的数据做一些基础转换,从而去适配新的 view 或交互逻辑)

更多关于 MVVM 的信息,参考以下文档: Implementing the MVVM Pattern.aspx) Advanced MVVM Scenarios.aspx)

译者按:

MVVM 作为2005年微软提出来的 UI 架构,我认为在经过这么多年的检验之后,还是非常值得信赖的。虽然在这一两年随着 React 的兴起,以及在前端领域“起死回生”的函数式编程,MVVM 被各种错误或‘恶意’的解读导致其花式被黑。但是在我看来,MVVM 作为一个完整的 GUI 架构,跟 Flux 流派的数据层架构本身理念上是并不冲突的。我们依然可以 M/VM 层实践 Flux。

我认为就前端领域而言,MVVM 最大的意义在于,如果我们能很干净的分离出应用的 view 和 M/VM,我们就可以使得整个应用的业务模型能独立于框架运行。相比于现在换一个框架就重写一次应用的做法(老实说我受够了而且觉得没什么价值),再结合当前前端‘欣欣向荣’的状态,如果能做到只需要花费很小的代价,我们就能快速的享受到新技术带来的红利,那基本上就非常美好了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80554.html

相关文章

  • JavaScript - 收藏集 - 掘金

    摘要:插件开发前端掘金作者原文地址译者插件是为应用添加全局功能的一种强大而且简单的方式。提供了与使用掌控异步前端掘金教你使用在行代码内优雅的实现文件分片断点续传。 Vue.js 插件开发 - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins译者:jeneser Vue.js插件是为应用添加全局功能的一种强大而且简单的方式。插....

    izhuhaodev 评论0 收藏0
  • 翻译:HTML5与HTML4的区别

    摘要:但由于翻译时草稿只发布了不到二十天,本文有很好的时效性。语法中同时定义了解析规则包括异常的处理方式。语法要求声明,以确保浏览器以标准模式渲染页面。语法中的声明为,不区分大小写。此外,仅允许一些标签上的属性设置。 本文选译自:W3C Working Group Note: HTML5 Differences from HTML4。 解释一下W3C Working Group Note,...

    Null 评论0 收藏0
  • 来自投资银行的 10 个 Java 面试题

    摘要:本文介绍一些来自投资银行的针对三年以上经验的开发人员面试题。第七题和这两个方法有什么不同答案本题取自我的投资银行针对有经验的开发者的五十个多线程面试题列表。总结以上就是投资银行通常会出的面试题。 原文地址: https://dzone.com/articles/10... 有为数不少的开发者希望能在像 Barclays、Credit Suisse、Citibank 等等那样的投资银行做...

    xbynet 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<