这个设计模式代码有味道吗?如何使用 DI 实现这一点?

Is this design pattern code smell? How to achieve this using DI?

我正在使用 MVVM 编写 WPF 应用程序。我的 ViewModels 非常大并且有很多与之相关的逻辑(过滤、搜索、写入数据库等),所以我决定尝试将 ViewModels 的逻辑分离到 "Presenter" class like 用于MVP。

所以,我的基本设置是这样的:

public class FooViewModel : ViewModelBase, IFooViewModel
{
     private IFooPresenter presenter;
     private ObservableCollection<FooModel> fooCollection;

     public FooViewModel()
     {
           presenter = FooPresenter(this);
     }

     public ObservableCollection<FooModel> FooCollection
     {
           get { return fooCollection; }
           set
             {
                  fooCollection = value;
                  OnPropertyChanged("FooCollection");
             }
     }

     public void FooCommandMethod(object obj)
     {
           presenter.DoStuff();
     }  
}

public class FooPresenter : IFooPresenter
{
    private IFooViewModel viewModel;

    public FooPresenter(IFooViewModel viewModel)
    {
        this.viewModel = viewModel;   
    }

    public void DoStuff()
    {
         viewModel.FooCollection.Add(new FooModel());

         //etc etc, make whatever ViewModel updates are needed
    }
}

我觉得这种循环依赖是不好的做法(View Model 依赖于 Presenter 而 Presenter 依赖于 View Model)。这些 classes 可以组合成一个大的 ViewModel class,但我确实喜欢这种方法使我的 View Models 保持干净的程度,它们所做的只是保存调用演示者函数的命令并保存 Model/collections 的模型。我也不喜欢 ViewModel 对 Presenter 的具体实现的依赖。我玩过的一种方法是使用服务定位器类型 class,所以它看起来像这样:

     public FooViewModel()
     {
           presenter = PresenterLocator.GetPresenter<IFooPresenter>(this);
     }

不过,我更喜欢在创建 ViewModel 时使用构造函数依赖注入来注入控制器。这样做的问题是,这会在 ViewModel 和 Presenter 的构造函数中产生循环依赖,这会导致我的应用程序在我尝试使用 Unity 实现此目的时崩溃。它最终看起来像这样:

     public FooViewModel(IFooPresenter presenter)
     {
           this.presenter = presenterl
     }

     public FooPresenter(IFooViewModel viewModel(
     {
          this.viewModel = viewModel;
     }

所以,我担心的是我的设计方法因此存在固有缺陷。尽管如此,我真的很喜欢它保持我的 ViewModels 并将它们与业务逻辑分开的干净程度。有没有更好的方法可以设计这个?有什么办法可以使用 DI 来实现这个目标吗?或者通过这样做,我实际上是在试图强制 DI 容器充当服务定位器吗?

首先,我不会称之为 "presenter"。这引入了一个不必要的混淆,事实上你的演示者没有展示任何东西,它只是从一个大视图模型中提取的一段代码。您是否考虑过将其命名为 "a service"?例如 SearchService

另一个问题是:这样的服务是否总是依赖于视图模型?或者更确切地说,它可以依赖于较低层(例如 works/repos 的单位)或其他服务吗?请注意,因为您的服务依赖于视图模型并且您直接在那里传递视图模型,所以您失去了对服务内部视图模型发生的情况的控制。您的 DoStuff 方法就是一个完美的例子,它对视图模型做了一些事情,改变了它的状态。相反,你可以

public class FooViewModel : ViewModelBase, IFooViewModel
{
  private IFooService service;
  private ObservableCollection<FooModel> fooCollection;

  public FooViewModel()
  {
     service = FooService(this);
  }

  public void FooCommandMethod(object obj)
  {
     // the responsibility on consuming service outcome is still here!
     this.FooCollection.Add( service.CreateNewModel() );
  }  
}

public class FooService : IFooService
{
  // constructor parameter not needed now
  public FooService()
  {
    this.viewModel = viewModel;   
  }

  public FooModel CreateModel()
  {
     return ...;
  }
}

如果您仍然坚持要有循环依赖,请使两者之一具有无参数构造函数和 属性 注入器:

public class FooViewModel : IFooViewModel
{
    private IFooService _service;
    public FooViewModel( IFooService service )
    {
        this._service = service;
        this._service.Model = this;
    }
}

public class FooService : IFooService
{
    public IFooViewModel Model { get; set; }
}

这样 Unity 请求 IFooViewModel 将解析无参数 IFooService 然后执行为双方设置循环的构造函数。