优雅地处理基类依赖项

时间:2017-11-29 02:25:50

标签: c# dependency-injection

在我正在进行的项目中,我添加了一个基本ViewModel类,其中包含所有ViewModel共有的一些功能和依赖项。它通过以下属性提供验证,消息传递,调度和导航服务:

IValidateProperties Validator { get; }
IMessenger Messenger { get; }
IDispatcherHelper DispatcherHelper { get; }
INavigationService Navigation { get; }

我使用IoC容器来连接我的依赖项,但是我有一些选项来处理这些依赖项,这些依赖项对于我的所有ViewModel都是通用的:

  1. 在构造函数中注入它们。如果我这样做,那么它需要将这四个参数添加到每个ViewModel的构造函数中,并将它们传递给基础构造函数。我的代码库中添加了很多额外的噪音,我真的很想避免。另外,如果我要在某个时刻添加另一个依赖项,则需要更改每个ViewModel的构造函数。

  2. 使用属性注入。这是我现在正在使用的方法。不幸的是,这意味着在构建ViewModel之后才能访问这些属性,从而导致以下解决方法:

    private IValidateProperties _validator;
    public IValidateProperties Validator
    {
        get => _validator;
        set
        {
            _validator = value;
            _validator.ValidationTarget = this;
        }
    }
    
    private IMessenger _messenger;
    public IMessenger Messenger
    {
        get => _messenger;
        set
        {
            _messenger = value;
            MessengerAttached();
        }
    }
    
    protected virtual void MessengerAttached() { }
    
  3. 将属性设为静态,并在应用启动时将其注入。对于MessengerDispatcherHelperNavigation来说这很容易,因为无论如何它们都被用作单身人士。对于Validator,我需要添加静态ValidatorFactory,并使用工厂在构造函数中实例化Validator。这种方式似乎是最干净的做事方式,但我有这个声音在我脑后告诉我,使用这样的静态是一个坏主意。

  4. 我觉得选项1是不可能的,因为它会导致添加到我的ViewModel中的大量嘈杂的样板代码。我仍然不确定2或3是否是最佳方式。在这种情况下,使用静力学是一个坏主意还是一个很好的理由,或者我是否在烦恼?

    有些人认为静态导致不可测试的代码,但在这种情况下,它实际上会使事情更容易测试。如果我使用选项3,我可以为我的所有视图模型测试添加以下类来继承:

    public abstract class ViewModelTestBase
    {
        protected readonly IValidateProperties ValidatorMock;
        protected readonly IMessenger MessengerMock;
        protected readonly IDispatcherHelper DispatcherHelperMock;
        protected readonly INavigationService NavigationMock;
    
        protected ViewModelTestBase()
        {
            ValidatorMock = Substitute.For<IValidateProperties>();
            ViewModelBase.Validator = ValidatorMock;
    
            MessengerMock = Substitute.For<IMessenger>();
            ViewModelBase.Messenger = MessengerMock;
    
            DispatcherHelperMock = Substitute.For<IDispatcherHelper>();
            ViewModelBase.DispatcherHelper = DispatcherHelperMock;
    
            NavigationMock = Substitute.For<INavigationService>();
            ViewModelBase.Navigation = NavigationMock;
        }
    }
    

    那么,有什么具体的理由不采用方法#3?如果在这种情况下静态确实是一个坏主意,那么有什么具体的理由不采用方法#2呢?

0 个答案:

没有答案