这个设计模式代码有味道吗?如何使用 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
然后执行为双方设置循环的构造函数。
我正在使用 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
然后执行为双方设置循环的构造函数。