0

在表示逻辑层 (PLL) 中,我有所谓的“视图模型”类,它们实现在业务逻辑层 (BLL) 中定义的 /extend 接口,如下所示:

namespace BLL.Abstract.POCO
{
    // also implemented in data access layer (DAL) by data model entities
    public interface ISomeDomainModelEntity
    {
        string Name { get; set; }
        string Description { get; set; }
    }
}

namespace PLL.Abstract.ViewModels
{
    public interface ISomeDomainModelEntityViewModel 
                     : ISomeDomainModelEntity, INotifyPropertyChanged
    {
        bool IsSelected { get; set; }
    }     
}

namespace PLL.Concrete.ViewModels
{
    public class SomeDomainModelEntityViewModel 
                 : ViewModelBase, ISomeDomainModelEntityViewModel
    {
        private string _name;
        private string _description;
        private bool _isSelected;

        public string Name { get { return _name; } set { _name = value; NotifyPropertyChanged(() => Name); } }
        public string Description { get { return _description; } set { _description = value; NotifyPropertyChanged(() => Description); } }
        public bool IsSelected { get { return _isSelected; } set { _isSelected = value; NotifyPropertyChanged(() => IsSelected); } }
    }
}

请注意,这ViewModelBase是一个允许这种花哨的强类型INotifyPropertyChanged实现的抽象类。

上述实现的IsSelected属性显然只需要在表示层中(可视化几十个这样的对象显示在一个列表中,ItemTemplate其中一个特性 aListBox将其IsChecked属性绑定到IsSelected),并且表示逻辑在返回业务之前知道如何使用它逻辑层。

我遇到的问题是,我发现名称“ViewModel”与此冲突:

namespace PLL.Abstract.ViewModels // hmmm...
{
    public interface ISomeWindowViewModel
    {
        ObservableCollection<ISomeDomainModelEntityViewModel> SomeItems { get; }
    }
}

的实现ISomeWindowViewModel最终分配给视图的DataContext属性:

namespace PLL.Concrete.ViewModels
{
    public class SomeWindowViewModel : WindowViewModelBase, ISomeWindowViewModel
    {
        private readonly ObservableCollection<ISomeDomainModelEntityViewModel> _items;

        public SomeWindowViewModel(IView view, ObservableCollection<ISomeDomainModelEntityViewModel> items)
            : base(view)
        {
            _items = items;
        }

        public ObservableCollection<ISomeDomainModelEntityViewModel> SomeItems { get { return _items; } set { _items = value; } }
    }
}

namespace PLL.Abstract
{
    public abstract class WindowViewModelBase : ViewModelBase
    {
        public IView View { get; private set; }

        protected WindowViewModelBase(IView view)
        {
            View = view;
            View.DataContext = this;
        }
    }
}

这是我在阅读 Mark Seeman 的.NET 中的 Dependency Injection 中关于如何将 DI 应用于 WPF 之后开始进行重大重构的结果(到目前为止读得很好,尚未完成),虽然起初它似乎有点倒退到将 View 实现注入到 ViewModel 中,我可以通过处理视图的显示和关闭来欣赏获得的控制/简单性。在重构之前,我有一个带有 BLL 的工作应用程序,它与 DAL 和 PLL 紧密耦合;现在 BLL 没有任何依赖关系,这太棒了。

我仍在重新配置组合根(使用 Ninject.Extension.Conventions),所以在一天结束时,我可能会意识到我需要进行一些调整以使其全部正常工作......并且它可能会受伤,但我已经准备好了!

所以问题如下:

  • 我觉得我的接口太多了,但是我完全不喜欢使用约定的 DI 配置(嗯,完全绿色的 DI!);当类型实现接口时,按惯例配置 IoC 容器似乎要容易得多……但这是否有点矫枉过正?
  • 这里是否过度使用了“ViewModel”这个术语?或者更确切地说,“ViewModel”和“WindowViewModel”之间的区别是有保证的还是我遗漏了什么?ViewModel “包含”其他 ViewModel 是否正常?如果没有,是否可以为在视图中用作 DataContext 的 ViewModel 提供一些“WindowViewModel”名称?
  • 这里是否有任何明显的架构缺陷最终会导致代码库变得一团糟?这个想法是尽可能可靠地使用 DI 实现 MVVM,但我认为我还有很多东西要学习。
  • 去年的这个问题在我输入这篇文章时出现在“类似问题”列表中,并且真的开始在我的脑海中进一步模糊 - 接受的答案和其他评论与我最近一直在阅读的内容完全矛盾。具体来说,应该为 ViewModel 类注入一些 IView 实现 - 这个问题/答案+评论的说法正好相反,这就是我最初所拥有的(注入了一些 IViewModel 实现的 View)。看起来这两种方式都是完全可以接受的,这只是个人喜好的问题?
4

1 回答 1

1
  1. 在我看来,太多了。架构应该是灵活的,当你闻到一些东西时,你可能不会一直坚持使用一种技术,比如 IOC。一方面我没有看到 ISomeDomainModelEntityViewModel、ISomeWindowViewModel 和 IObservableCollection 的好处,每个接口只有一个类,除了 DI,另一方面,接口对于 DI 不是必需的。我想你可以看看 Prism 示例,看看它们如何使用 DI 构建 ViewModel 以及何时使用接口。通常情况下,如果要使用 DI 构造 ViewModel,ViewModel 的所有依赖都是服务,定义在接口中,并使用 IOC 来解析这些服务。由于这些服务喜欢具有外部依赖关系,因此模拟这些接口进行测试很容易。然而,

  2. 我认为 ViewModel 可以包含其他 ViewModel,例如一个主 ViewModel 包含每个页面的子项。

  3. 不明显。除了接口,我个人认为将 IObservableCollection 放在 IOC 中并不是一个好方法。

  4. 我个人认为注入 IView 会破坏 MVVM,因为 ViewModel 不应该知道 View。即使是 IView,ViewModel 也有对 View 的引用。但是在某些情况下,如果 ViewModel 有一个 IView 可以让很多事情变得更容易,为什么不呢?正如我所说,架构应该是灵活的。

希望它可以提供帮助。

于 2013-07-11T21:57:48.510 回答