每次 ModelView 在 MVVM 中使用 Model 时,我都需要使用 DI 和 IoC 吗?
Do I need to use DI and IoC everytime ModelView uses Model in MVVM?
我是 .NET MVVM 应用程序结构的新手,只了解其原理的基本知识,例如解耦、绑定、命令等。我正在使用 MVVM Light 框架来简化常见的 MVVM 问题,例如消息传递和服务位置。
有一件事我不明白:我每次使用 ViewModel 中的模型 classes 时都需要调用 SimpleIoC 吗?
例子:
我有一个简单的视图,一个与之对应的视图模型和一个带有 class Settings
.
的模型
MainWindow.xaml
<Window ...>
<Window.Resources>
<viewModels:MainWindowModel x:Key="ViewModel" />
</Window.Resources>
<DockPanel DataContext="{StaticResource ViewModel}">
<Button Command="{Binding DoSomeCommand}" />
</DockPanel>
</Window>
MainWindowModel.cs
public class MainWindowModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand DoSomeCommand { get; private set; }
protected void RaisePropertyChangedEvent(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowModel()
{
DoSomeCommand = new RelayCommand(DoSome);
}
public void DoSome()
{
var data = Settings.Instance; //singleton
//... do some with data ...
Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
Settings.cs
public static class Settings
{
//... singleton implementations ...
public int Prop1 { get; set; } // implementation of getters and setters
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
此代码有一个巨大的缺点:DoSome()
方法不可单元测试。好的,让我们解决这个问题:
public class MainWindowModel: INotifyPropertyChanged
{
//...
private Settings _data;
public MainWindowModel()
{
_data = Settings.Instance;
DoSomeCommand = new RelayCommand(() => DoSome(_data));
}
public void DoSome(Settings data)
{
//... do some with data ...
Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
现在我可以模拟或存根 Settings
class 并测试 DoSome()
。
但我知道 'Settings' class 可能有不同的实现方式,例如 'SettingsXML'(XML 的数据),'SettingsRegistry'(Window 注册表),'SettingsINI'(来自 INI 文件的数据,奇怪,但真实)。为了避免潜在的问题,我在接口中重写了它:
public interface ISettings
{
public int Prop1;
public int Prop2;
public int Prop3;
}
public static class Settings: ISettings
{
//... singleton implementations ...
public int Prop1 { get; set; } // implementation of getters and setters
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
public class MainWindowModel: INotifyPropertyChanged
{
//...
private ISettings _data;
public void DoSome(ISettings data)
{
... do some with data ...
Debug.Log($"_data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
我觉得一切都很好。 DoSome()
是可测试的,Settings
实现可能不同。有一件事困扰着我:MainWindowModel
知道实际的 class 设置 (_data = Settings.Instance
)。
MVVM结构可以吗?
真的有必要使用一些 IoC,写一些 'SettingsWrapper' class 依赖注入 ISettings class 然后使用 _data = SimpleIoc.Default.GetInstance<SettingsWrapper>
吗?
如果 Settings
class 不是单身人士,我该怎么办?
抱歉,如果我对 DI 和 IoC 的基本理解完全错误。如果您能纠正我,我将不胜感激。
One thing bothers me: MainWindowModel
knows actual class of settings (_data = Settings.Instance
).
不应该。它应该只知道它正在注入的接口。
不是将 ISettings
对象传递给 DoSome
方法,而是可以在创建 MainWindowModel
class 本身时用 ISettings
注入它:
public MainWindowModel(ISettings settings) { ... }
然后您可以让 ViewModelLocator
负责创建视图模型 class:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<ISettings>(() => Settings.Instance);
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
}
<DockPanel DataContext="{Binding Main, Source={StaticResource Locator}}">
<Button Command="{Binding DoSomeCommand}" />
</DockPanel>
我是 .NET MVVM 应用程序结构的新手,只了解其原理的基本知识,例如解耦、绑定、命令等。我正在使用 MVVM Light 框架来简化常见的 MVVM 问题,例如消息传递和服务位置。
有一件事我不明白:我每次使用 ViewModel 中的模型 classes 时都需要调用 SimpleIoC 吗?
例子:
我有一个简单的视图,一个与之对应的视图模型和一个带有 class Settings
.
MainWindow.xaml
<Window ...>
<Window.Resources>
<viewModels:MainWindowModel x:Key="ViewModel" />
</Window.Resources>
<DockPanel DataContext="{StaticResource ViewModel}">
<Button Command="{Binding DoSomeCommand}" />
</DockPanel>
</Window>
MainWindowModel.cs
public class MainWindowModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand DoSomeCommand { get; private set; }
protected void RaisePropertyChangedEvent(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowModel()
{
DoSomeCommand = new RelayCommand(DoSome);
}
public void DoSome()
{
var data = Settings.Instance; //singleton
//... do some with data ...
Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
Settings.cs
public static class Settings
{
//... singleton implementations ...
public int Prop1 { get; set; } // implementation of getters and setters
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
此代码有一个巨大的缺点:DoSome()
方法不可单元测试。好的,让我们解决这个问题:
public class MainWindowModel: INotifyPropertyChanged
{
//...
private Settings _data;
public MainWindowModel()
{
_data = Settings.Instance;
DoSomeCommand = new RelayCommand(() => DoSome(_data));
}
public void DoSome(Settings data)
{
//... do some with data ...
Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
现在我可以模拟或存根 Settings
class 并测试 DoSome()
。
但我知道 'Settings' class 可能有不同的实现方式,例如 'SettingsXML'(XML 的数据),'SettingsRegistry'(Window 注册表),'SettingsINI'(来自 INI 文件的数据,奇怪,但真实)。为了避免潜在的问题,我在接口中重写了它:
public interface ISettings
{
public int Prop1;
public int Prop2;
public int Prop3;
}
public static class Settings: ISettings
{
//... singleton implementations ...
public int Prop1 { get; set; } // implementation of getters and setters
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
public class MainWindowModel: INotifyPropertyChanged
{
//...
private ISettings _data;
public void DoSome(ISettings data)
{
... do some with data ...
Debug.Log($"_data.Prop1}, {data.Prop2}, {data.Prop3}");
}
}
我觉得一切都很好。 DoSome()
是可测试的,Settings
实现可能不同。有一件事困扰着我:MainWindowModel
知道实际的 class 设置 (_data = Settings.Instance
)。
MVVM结构可以吗?
真的有必要使用一些 IoC,写一些 'SettingsWrapper' class 依赖注入 ISettings class 然后使用 _data = SimpleIoc.Default.GetInstance<SettingsWrapper>
吗?
如果 Settings
class 不是单身人士,我该怎么办?
抱歉,如果我对 DI 和 IoC 的基本理解完全错误。如果您能纠正我,我将不胜感激。
One thing bothers me:
MainWindowModel
knows actual class of settings (_data = Settings.Instance
).
不应该。它应该只知道它正在注入的接口。
不是将 ISettings
对象传递给 DoSome
方法,而是可以在创建 MainWindowModel
class 本身时用 ISettings
注入它:
public MainWindowModel(ISettings settings) { ... }
然后您可以让 ViewModelLocator
负责创建视图模型 class:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<ISettings>(() => Settings.Instance);
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
}
<DockPanel DataContext="{Binding Main, Source={StaticResource Locator}}">
<Button Command="{Binding DoSomeCommand}" />
</DockPanel>