我最近开始深入研究MVVM架构模式。我已经理解了它的大部分内容,但仍然很少有人怀疑 ViewModel应该代表View 承担多少责任。
换句话说,视图应该是多么愚蠢?
例如,对于简单的状态协调,例如在用户按下TextView
后清除SubmitButton
。这种状态协调只需要使用一些流行的数据绑定框架来实现。
例如伪 - ReactiveCocoa:
textView.text <~ submitButton.pressed.map { _ in "" }
然而,Martin Fowler在他的Presentation Model中写道,
表示模型包含的逻辑表明,只有选中复选框才能启用composer字段,因此当视图从Presentation Model更新时,composer字段控件会更改其启用状态
这表明即使按TextView
后清除Button
这样的简单逻辑也应封装在ViewModel
内。
这样的设计选择导致类似这样的事情(再次,在伪ReactiveCocoa中):
// In View
viewModel.submitButtonPressed <~ submitButton.pressed
textView.text <~ viewModel.textViewText
// In ViewModel
textViewText <~ viewModel.submitButtonPressed.map { _ in "" }
虽然它假设视图只有绑定工作(使其变得愚蠢)时能够更好地封装逻辑,但它确实使代码更加冗长并导致View
和{{1}之间更紧密的耦合(要求ViewModel
了解ViewModel
)。
我一般都是MVVM模式的新手,每天都在学习东西。
ViewModel应承担多少责任?
换句话说,SubmitButton
必须完全愚蠢,只处理简单绑定(将其UI元素连接到View
提供的可绑定属性),或者ViewModel
可以公平处理像上面的简单逻辑?
另一方面,MVVM中View
和View
好的之间的紧密联系是什么?
答案 0 :(得分:5)
一般来说,ViewModel
承担全部责任。更具体地说,在您的方案中,ViewModel
不会知道submitButton
,而View
会知道ViewModel
会公开命令({{1}调用ICommand
,并将SubmitCommand
绑定到该命令。
有时可以更多地涉及完全分离动作和相应的逻辑,例如当没有可用于特定事件的命令的绑定时。但在这些情况下,一个相当简单的附加行为(即submitButton
和朋友,请参阅the documentation)可以将这个差距缩小到同轴流,因此逻辑可以进入InvokeCommandAction
。
很少有情况(目前没有想到的),其中所以涉及到我只是跳过整个想法,并尽可能地分开,而不是必须三个月后正好到底是怎么回事。但这些案件确实很少见。
答案 1 :(得分:2)
换句话说,View必须完全愚蠢,只能处理简单的绑定
当视图仅包含数据绑定时,它非常好,但IRL复杂视图可以包含一些特定于视图的逻辑。例如,由于单视图模型可以连接到多个视图,因此焦点管理是视图的特权。另一个示例是“如果元素B被禁用则隐藏元素A”或“如果选中按钮则将颜色从A更改为B”等逻辑,等等。
XAML框架提供了几种技术,使视图逻辑组合得更好:命令,触发器,附加行为,值转换器。但有时你实际上需要代码隐藏。
例如,对于简单的状态协调,例如清除TextView 用户按下SubmitButton
后
更清楚。 这不是视图逻辑,必须放在视图模型中:
public class ViewModel
{
private string someText;
public string SomeText
{
get { return someText; }
set
{
if (someText != value)
{
someText = value;
OnPropertyChanged();
}
}
}
private ICommand submitCommand;
public ICommand SumbitCommand
{
if (submitCommand == null)
{
submitCommand = new RelayCommand(() =>
{
// do submit
// clear text
SomeProperty = null;
});
}
return submitCommand;
}
}
XAML:
<TextBox x:Name="SomeTextBox" Text="{Binding SomeText}"/>
<Button Content="Submit" Command="{Binding SubmitCommand}">
但这是一个视图逻辑:
public MyWindow()
{
InitializeComponent();
// SomeTextBox should have initial focus
Loaded += (sender, args) => SomeTextBox.Focus();
}
在MVVM中View和ViewModel之间是否紧密耦合?
理想情况下,所有组件都应松散耦合,但视图必须了解视图模型属性才能执行数据绑定。