当前的 MVVM 视图模型是不是违反了单一职责原则?
Posted
技术标签:
【中文标题】当前的 MVVM 视图模型是不是违反了单一职责原则?【英文标题】:Are current MVVM view model practices a violation of the Single Responsibility Principle?当前的 MVVM 视图模型是否违反了单一职责原则? 【发布时间】:2011-11-02 03:01:49 【问题描述】:根据当前的做法(至少使用 WPF 和 Silverlight),我们看到通过视图模型中的命令绑定绑定的视图,或者我们至少看到在视图模型中处理的视图事件。这似乎违反了SRP,因为视图模型不仅对视图状态建模,而且对视图(用户)做出响应。其他人问过how to build view models without violating SRP或者问过whether their implementations do so(最后这个是MVC中的控制器,但大致类似)。
那么当前的做法是否违反了 SRP?还是“视图模型”真的是不违反 SRP 的东西的集合?稍微概括一下,似乎我们需要知道什么是单一职责,或者概念中是否有多个职责,是否将各个职责分开,符合 SRP。我不确定。
Wikipedia's definition of view model 说
[T]ViewModel 是“视图的模型”,这意味着它是视图的抽象,也用于视图和模型之间的数据绑定
这对于 SRP 来说似乎已经足够了,但是后来的条目说(我的重点补充了)
[ViewModel] 充当数据绑定器/转换器,将模型信息更改为视图信息并将命令从视图传递到模型
在关于视图模型角色的Prism blog post 中,作者说(再次强调)
归结起来,视图模型是以下内容的组合:
视图的抽象 命令 价值转换器 查看状态
我确定我错过了很多定义,但它们似乎属于以下类别:
-
建模视图状态的单一“模糊”责任(那我们该怎么办?
意思按州)
多重职责(视图状态、用户交互(即
命令))
单个特定职责的组合(抽象、
状态、交互、转换),因此只有一个
责任:“管理所有这些东西”。
如果您好奇,我会“关心”这一点,因为 (2) 感觉不错,但似乎与流行的实现背道而驰。
【问题讨论】:
你知道,实际上可以在 MVVM 中让命令成为第四方,它知道 View 和 ViewModel,并且可以在两者之间进行调解。在 xaml 中编写您的虚拟机和命令时,它工作得非常好...... @Will - 这就是我要去的地方。在服务器端代码和 MVP 客户端中断之后,MVVM 对我来说有点新。我原以为视图会执行命令,命令会进行服务调用和操作视图模型,视图模型(通过绑定)会更新视图。 【参考方案1】:单一职责为Martin defines it:
“改变课程的理由不应该不止一个。”
就 MVVM 而言,ViewModel 实际上只是 Presentation Model 的专门实现。
因此,尽管有人认为 Presentation Model 应该只代表 UI 的 状态,而 Presenter/Controller 应该始终在 UI 和 Presentation Model 之间代理命令。如果遵循这个想法,SRP 划分状态和命令,那么添加命令应该不会影响表示状态的类。因此 MVVM 会破坏 SRP。
但是...
我认为这是在抓住稻草。 MVVM 是一种相当专业的实现,主要用于 WPF/Silverlight (and now browser clients)。
模式旨在使设计更简单,替代方案会更麻烦或更难维护。由于 MVVM 旨在利用表示技术极其丰富的数据绑定功能,因此这是一个值得权衡的选择。
【讨论】:
【参考方案2】:不! MVVM 不违反 SRP,(程序员确实如此,哈哈!)
没有理由使用 MVVM 模式需要忽略 SRP。 MVVM 并不意味着只有一个 Model Class、一个 View-Model Class 和一个 View Class。当然,如果你只有一个 View Class,你就只能显示一个简单的屏幕。
视图层中的那些类应该负责一件事;做、决定或包含。一个视图可以由几个子视图组成,它们的工作是完成某些用户交互。考虑一个基本的表单,它有一个显示网格,网格中的项目可以编辑,还有一个“保存”按钮。
主视图将是其他两个视图的容器;数据网格(用户控件或其他东西)和命令控件。然后数据网格负责选择正确的子视图来呈现数据;从本质上讲,它是一个数据绑定的容器。用于编辑项目的视图是数据网格的子视图,而数据网格又是主视图的子视图。最后,命令控件是一组按钮(在本例中为单个按钮),它们的唯一职责是发出用户已发出命令的信号。
通过这种方式,编辑视图(由 DataGrid 使用)对于使用它的内容是不可知的,并且有一个责任;与命令控制相同。同样,DataGrid 不关心谁使用它,只关心它可以包含编辑视图(子视图)。不错的 SRP。
与视图(和子视图)匹配的视图模型也负责一件事。编辑视图模型是编辑视图绑定的容器;它只包含可以显示/编辑的数据字段。它不关心任何事情,只关心其属性之一发生变化时的信号。命令按钮视图模型是一个做事的类。它的命令绑定到按钮,它会根据用户点击的内容来工作。它必须能够访问 ViewModel(s) 的其他部分才能完成工作。
主页视图模型用于包含其他子视图。它的唯一职责是作为初始化程序,创建所有必需的 ViewModel 实例,并将构造函数参数传递给其他 ViewModel 实例(例如,命令按钮视图模型,以便它知道从哪里获取数据以进行工作)
将一大堆功能塞进一个大视图将绑定到的单个 ViewModel 是很自然的。但不一定非得如此,SRP 可以在 MVVM 中维护。
MVVM 的主要目标是允许可测试的设计,Model 层可以独立测试,Model 中的所有类都可以轻松遵循 SRP。 ViewModel 可以在不需要视图的情况下进行测试;在 ViewModel 中考虑 SRP 变得更加棘手,但它肯定是可行的;只要记住打破你的课程,这样他们就只有一个问题。视图绑定到部分视图模型,如果运气好的话,您对视图模型的测试使得捕捉视图变得超级容易。请记住,您可以让每个 View-let 都遵守 SRP,成为更大的综合视图(容器)的一部分。
TL;博士? 为了直接回答您的问题,视图是不破坏 SRP 的类的集合。因此,当 ViewModel 从 View(s) 中抽象出来时(View-First),它们也是遵循良好 SRP 的类的集合。
【讨论】:
【参考方案3】:我认为当前围绕 MVVM 的许多做法(至少)违反了 SPR。这是另一种情况,只需将控制器添加回 MVVM 即可干净地解决所有问题。我称之为 MVCVM :)
我们在最近的所有项目中成功使用的模式是仅在模块中注册控制器,并在启动时对其进行初始化。控制器非常轻巧/纤薄,是应用程序生命周期中唯一需要监听或发送消息的东西。然后在他们的初始化方法中注册他们需要拥有的任何东西(视图和视图模型等)。这种轻量级的仅内存逻辑模式也使应用程序更轻薄(例如,更适合 WP7)。
正如您所发现的,仅使用 VM 的问题在于,最终您会遇到他们需要了解视图、命令或其他不应该涉及自尊的 ViewModel 的东西的情况!
我们遵循的基本规则是:
控制器根据事件做出决策 控制器获取数据并将其放置在适当的视图模型属性中 控制器设置视图模型的 ICommand 属性以拦截事件 控制器使视图出现(如果其他地方没有暗示) 视图模型是“愚蠢的”。用于绑定的保持数据和nothing else 视图知道它们显示某种形状的数据,但不知道它来自哪里最后两点是你永远不应该打破的,否则关注点分离会消失。
简单地将控制器添加回 MVVM 组合似乎可以解决我们发现的所有问题。 MVVM 是个好东西,但为什么它们不包括控制器呢? (但这当然只是我的看法) :)
【讨论】:
确实感觉视图模型做得太多了。如果它们用于操作视图,那么它们不应该嵌入与...服务对话的命令。 “控制器设置 ICommand...拦截事件”是什么意思? @Kit:ViewModel 包含用于绑定命令事件的 ICommand 属性,但它们指向的操作(它们运行的代码)应由控制器确定/设置。 我也想念 WPF-MVVM 中数据绑定(包括 ICommand)的管理或控制。我认为 WPF 数据绑定实际上是控制器部分,我们无法以清晰或干净的方式对其进行管理。您知道 WPF 示例,其中完成了某种数据绑定和 ICommand 控制吗? 您不需要“控制器”(如在 MVC 中),您可以在另一个 ViewModel 中拥有一个 ViewModel,甚至在另一个 ViewModel 中拥有多个 ViewModel。【参考方案4】:归结起来,视图模型是以下内容的组合:
视图的抽象 命令 价值转换器 查看状态
我不明白您为什么将前两项分开。命令是视图的一部分。
至于其余的 - 你是对的。在某些情况下。我已经构建了应用程序,其中值转换和维护视图状态的任务非常复杂,以至于单个视图模型类无法完成所有工作,我将它们分解为与 VM 互操作的单独类。
那么?
【讨论】:
我没有将它们分开,但我确实明白你的意思。似乎责任的问题(如“是单一的还是双重的”)似乎是围绕状态建模与指挥。我已经在许多实现中看到了转换,所以我将放弃它作为“决定者”。以上是关于当前的 MVVM 视图模型是不是违反了单一职责原则?的主要内容,如果未能解决你的问题,请参考以下文章