每次 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>