在 MVVM 中解耦视图模型

本文关键字:视图 模型 解耦 MVVM | 更新日期: 2023-09-27 18:34:46

我知道在MVVM模式(或者可能在任何此类设计模式中(中,我们应该保持我们的层解耦。根据我的理解,这也意味着,我应该将我的ViewModels分开。我在遵循此规则时遇到了一些麻烦。

说 - 我有一个ConversationViewModel和一个MessageViewModel - 前者需要创建后者的实例。当ConversationViewModel收到有关传入消息的通知时,它会生成一个新的MessageViewModel实例并用数据填充它。

问题是 - 如果我在ConversationViewModel中显式创建新的MessageViewModel实例,是否会使我的应用程序更难测试?我的意思是 - 一个代码单元是ConversationViewModel,另一个是MessageViewModel - 我想分别测试两者,所以当有人在后面破坏某些东西时,测试前者不会受到影响。我如何实现它?

我正在使用 MVVMLight,所以我想我会MessageViewModel注册为某个接口的实现,然后创建一个类,如MockMessageViewModel实现相同的接口,但仅在测试中使用。然后在ConversationViewModel中,我会要求 IOC 容器只给我注册的实现。这是一个好方法,还是我反应过度?示例代码:

public class ViewModelLocator {
    public ViewModelLocator() {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        if (//in test) {
            SimpleIoc.Default.Register<IMessageViewModel, MockMessageViewModel>();
        }
        else {
            SimpleIoc.Default.Register<IMessageViewModel, MessageViewModel>();
        }
}
public class ConversationViewModel : ViewModelBase {
    public void MessageReceived(string data) {
        //I'm thinking about doing this:
        var vm = SimpleIoc.Default.GetInstance<IMessageViewModel>();
        // instead of doing this
        var vm = new MessageViewModel();
        //do stuff with vm
    }
}

在 MVVM 中解耦视图模型

是否使用接口基础方法将视图模型彼此分离是基于应用程序复杂性的设计决策。如果要在 IConvesationViewModel 中动态创建 IMessageViewModel 的实例;我建议不要在ViewModel类中引用IoC容器,而是注入一个工厂,以便在ConversationViewModel构造函数中创建IMessageViewModel。稍后,您可以使用此工厂创建 IMessageViewModel 的实例。工厂的简单实现可以是 Func 委托。

public class ConversationViewModel 
{
    private Func<IMessageViewModel> _messageViewModelFactory;
    public ConversationViewModel(Func<IMessageViewModel> messageViewModelFactory)
    {
        _messageViewModelFactory = messageViewModelFactory;
    }
    public void MessageReceived(string data) {
        var messageViewModel = _messageViewModelFactory();
    }
}

这样,您就可以通过构造器公开 ConversationViewModel 类的依赖项,而不是将它们隐藏在类实现中。像Autofac这样的IoC容器提供了在构造函数中注入Func的方法,当你使用它创建ConversationViewModel的对象时。

我相信

更好的方法是使用接口。您可以让真实视图模型和模拟视图模型实现相同的接口,并在使用 ViewModel 类的任何位置使用该接口。

如果是

我,我可能没有关于您的应用程序的所有信息,但我将有一个视图模型 IConversationViewModel。 在IConversationViewModel中,我将有一个IMessageModel实例的集合。 我不会去嵌套视图模型。

您可以

做的是立即在ViewModelLocator中创建MessageViewModel,并使用其构造函数中的 MVVMLight MessengerInstance注册以接收MessageViewModel中的消息。像这样:

public class ViewModelLocator
{
    public class ViewModelLocator()
    {
       //creates instance immediately
       SimpleIoc.Default.Register<MessageViewModel>(true);
    }
}
public class MessageViewModel:ViewModelBase
{
     public MessageViewModel()
     {
        MessengerInstance.Register<string>(this,DoSomething);
     }
     public void DoSomething(string data)
     {
             //do stuff
     }
}
public class ConversationViewModel:ViewModelBase
{
     public void MessageReceived(string data)
     {
       MessengerInstance.Send<string>(data);//this will trigger DoSomething in MessageViewModel
     }
}