当前的MVVM视图模型实践是否违反了单一职责原则?



在当前的实践中(至少在WPF和Silverlight中),我们看到视图通过命令绑定在视图模型中绑定,或者我们至少看到视图事件在视图模型中处理。这似乎违反了SRP,因为视图模型不仅对视图状态建模,而且还响应视图(用户)。其他人问如何在不违反SRP的情况下构建视图模型,或者问他们的实现是否这样做(最后一个是MVC中的控制器,但大致类似)。

那么当前的做法是否违反了SRP?或者是"视图模型";真的是不违反SRP的东西的集合吗?为了构建这个框架,我们似乎需要知道什么是单个职责,或者在概念中是否有多个职责,是否按照SRP将单个职责分离出来。我不确定。

维基百科对视图模型的定义是

[T] ViewModel是"视图的模型",这意味着它是视图的抽象,也在视图和模型之间提供数据绑定

对于SRP来说,这似乎已经足够好了,但是后来的条目说(我强调了)

[The ViewModel]充当数据绑定器/转换器,将模型信息转换为视图信息,并将视图的命令传递给模型

在一篇关于视图模型角色的Prism博客文章中,作者说(再次强调)

归结起来就是视图模型是以下的组合:

  • 视图的抽象
  • <
  • 命令/gh>
  • 值转换器
  • viewstate

我肯定我错过了很多定义,但它们似乎可以归为以下几类:

  1. 单"vague"建模视图状态的责任(所以我们各州平均值
  2. 多重责任(视图状态,用户交互)命令)
  3. 单个特定职责(抽象、状态、交互、转换),因此具有单个责任:"管理所有的东西"。

如果你很好奇,我很关心。关于这一点,因为(2)感觉是对的,但似乎与流行的实现相反。

Martin定义的单一责任:

"一个类永远不应该有多于一个的改变原因。"

就MVVM而言,ViewModel实际上只是表示模型的一个专门实现。

因此,虽然可以认为表示模型应该只表示UI的状态,而表示器/控制器应该始终代理UI和表示模型之间的命令。如果遵循这个想法,使用SRP划分State和Commands,那么添加一个命令应该不会影响表示State的类。因此MVVM会破坏SRP。

然而…

我认为这是抓救命稻草。MVVM是一个相当专业的实现,主要用于WPF/Silverlight(现在还有浏览器客户端)。

模式是为了使设计更简单而设计的,否则会更麻烦或更难以维护。由于MVVM的设计是为了利用表示技术的极其丰富的数据绑定功能,因此它是值得权衡的。

我认为当前围绕MVVM的许多实践违反了SPR(至少)。这是另一种情况,只需将控制器添加回MVVM就可以清楚地解决所有问题。我叫它MVCVM:)

我们在最近的所有项目中成功使用的模式是在模块中仅注册控制器,并在启动时初始化它们。控制器非常轻/薄,并且是唯一需要在应用程序生命周期中监听或发送消息的东西。然后在他们的initialize方法中注册任何他们需要拥有的东西(视图和视图模型等)。这种轻量级的逻辑仅在内存中的模式也使得应用程序更精简(例如更适合WP7)。

正如你所发现的那样,仅仅使用vm的问题是,最终你会遇到需要了解视图、命令或其他没有自尊的ViewModel应该参与的事情的情况!

我们遵循的基本规则是:

  • 控制器根据事件做出决策
  • 控制器获取数据并将其放置在适当的视图模型属性
  • 控制器设置视图模型的命令属性来拦截事件
  • 控制器使视图显示(如果没有在其他地方暗示)
  • 视图模型是"哑的"。为绑定和nothing保留数据 else
  • 视图知道它们显示某种形状的数据,但不知道它从哪里来

最后两点是你永远不应该打破的,否则关注点分离就会消失。

简单地将控制器添加回MVVM组合中似乎可以解决我们发现的所有问题。MVVM是个好东西,但为什么它们不包含控制器呢?(但这当然只是我的意见):)

不!MVVM不违反SRP,(程序员违反了,哈哈!)

没有理由使用MVVM模式需要忽略SRP。MVVM并不意味着只有一个模型类、一个视图模型类和一个视图类。当然,如果你只有一个视图类,你就只能显示一个简单的屏幕。

那些在视图层的类,应该负责一件事;做的,决定的或包含的一个视图可以由几个子视图组成,这些子视图的任务是完成用户交互的某些部分。考虑一个基本的表单,它有一个显示网格,网格中的项目可以编辑,并且有一个"保存"按钮。

主视图是另外两个视图的容器;数据网格(用户控件等)和命令控件。然后,数据网格负责选择正确的子视图来呈现数据;从本质上讲,它是一个数据绑定的容器。编辑项的视图是数据网格的子视图,而数据网格又是主视图的子视图。最后,命令控件是一组按钮(在本例中是单个按钮),其唯一职责是发出用户发出命令的信号。

这样,编辑视图(由DataGrid使用)就不知道是什么在使用它,只有一个责任;命令控件也是如此。同样,DataGrid不关心谁使用它,只关心它可以包含编辑视图(子视图)。不错的SRP

匹配视图(和子视图)的ViewModels也负责一件事。编辑视图模型是编辑视图绑定的容器;它只包含可以显示/编辑的数据字段。它不关心任何事情,只关心当它的一个属性改变时发出信号。命令按钮视图模型是一个做事情的类。它的命令被绑定到按钮上,它将根据用户点击的内容来工作。它必须能够访问ViewModel的其他部分来完成它的工作。

主页视图模型是用来包含其他子视图的。它的唯一职责是作为初始化器,创建所有所需的ViewModel实例,并将构造函数参数传递给其他ViewModel实例(例如,命令按钮视图模型,以便它知道从哪里获取数据用于它的工作)

把一大堆功能塞进一个ViewModel是很自然的,一个大的视图将绑定到这个ViewModel。但其实不必这样,SRP可以在MVVM中维护。

MVVM的主要目标是允许可测试的设计,模型层可以独立测试,模型中的所有类可以很容易地遵循SRP。无需视图就可以测试ViewModel;在ViewModel中考虑SRP会变得更加棘手,但它肯定是可行的;只要记得把你的班级分开,这样他们就只关心一个问题。视图与ViewModels绑定在一起,如果运气好的话,你对ViewModel的测试可以使视图的捕捉变得非常容易。请记住,您可以让每个View-let遵守SRP,使其成为更大的聚合视图(容器)的一部分。

TL,博士?直接回答您的问题,视图是不违反SRP的类的集合。因此,当ViewModel从视图(View- first)中抽象出来时,它们也是遵循良好SRP的类集合。

它归结为视图模型是以下内容的组合:

  • 视图的抽象
  • <
  • 命令/gh>
  • 值转换器
  • viewstate

我不明白你为什么把前两项分开。命令是视图的一部分。

至于其余的,你是对的。在某些情况下。我已经构建了一些应用程序,其中值转换和维护视图状态的任务非常复杂,单个视图模型类无法完成所有任务,因此我将它们分解为与vm互操作的单独类。

?

最新更新