由于开闭原则处理继承层次

Dealing with inheritance hierarchy because of Open-Closed Principle

当我尝试遵循 Open-Closed Principle (OCP) 时,在实现的用例数量之后,我总是以继承的 classes 层次结构结束。通常它发生在 MVVM 结构中的 ViewModels 中,因为它们发生了很多变化。例如(C#,但可以是任何其他面向 class 的语言):

internal class MyAwesomeViewModel
{
    private IDependency1 _dependency1;

    public string Property1 { get; set; }
    public ICommand Command1 { get; }

    public MyAwesomeViewModel(IDependency1 dependency1)
    {
        _dependency1 = dependency1;
        Command1 = new DelegateCommand(...);
    }
}

internal class MyAwesomeViewModelWithAnotherProperty: MyAwesomeViewModel
{
    public string Property2 { get; set; }

    public MyAwesomeViewModelWithAnotherProperty(IDependency1 dependency1)
        :base(dependency1)
    {
    }
}

internal class MyAwesomeViewModelWithNewCommandAndDependency: MyAwesomeViewModelWithAnotherProperty
{
    private IDependency2 _dependency2;

    public ICommand Command2;

    public MyAwesomeViewModelWithNewCommandAndDependency(IDependency1 dependency1, IDependency2 dependency2)
    : base(dependency1)
    {
        _dependency2 = dependency2;
        Command2 = new DelegateCommand(...);
    }
}

internal class MyAwesomeViewModelWithBunchNewProperties : MyAwesomeViewModelWithNewCommandAndDependency
{
    public int IntProperty { get; }
    public bool BoolProperty { get; }
    public double DoubleProperty { get; }

    public MyAwesomeViewModelWithBunchNewProperties(IDependency1 dependency1, IDependency2 dependency2)
    : base(dependency1, dependency2)
    {
    }
}

问题是,当涉及到 4 级和更多级别的继承深度时,它变得非常混乱且难以阅读。我也经常遇到命名问题,这是错误组合的信号(比如 MainWindowViewModel,然后是 MainWindowViewModelCloseCommand,然后是 MainWindowViewModelUserRelatedProperties 等等)。

因为只使用了最后一个派生的 class(MyAwesomeViewModelWithBunchNewProperties 在上面的例子中),有时我考虑在发布或其他重要里程碑之前将所有继承压缩到1 class,像这样:

internal class MyAwesomeViewModel
{
    public int IntProperty { get; }
    public bool BoolProperty { get; }
    public double DoubleProperty { get; }
    public string Property1 { get; set; }
    public string Property2 { get; set; }
    public ICommand Command1 { get; }
    public ICommand Command2;

    public MyAwesomeViewModelWithBunchNewProperties(IDependency1 dependency1, IDependency2 dependency2)
    {
        _dependency1 = dependency1;
        _dependency2 = dependency2;
        Command1 = new DelegateCommand(...);
        Command2 = new DelegateCommand(...);
    }
}

但它可能违反单一职责原则 (SRP) 并导致非常大的超类。

问题:如何处理多继承问题?或者这根本不是问题,有这种类型的 classes 结构就可以了?

为了让 OCP 为您服务,您应该确定您的客户需要的行为。这些知识可用于构建一个或多个您的 ViewModel 类 然后实现的接口。为了避免您的继承层次结构,您可以更喜欢组合以

的方式构建您的 ViewModel

SOLID 原则 ideas/guidelines 可帮助您提出更好的解决方案。遵循这些原则不会天生获得任何好处。

此处发布的继承策略效果不佳。它无济于事,并导致混乱和更多的工作。这不是一个好方法。

But it could violate Single Responsibility Principle (SRP) and lead to very large SuperClass.

SRP 在 "single responsibility" 是什么方面非常模糊。您可以根据需要将其定义为窄或宽。同样,该原则只是为了指导您,让您考虑不要不必要地混合应该分开的东西。

在这里,你可以说"The responsibility of this class is to be the model for data binding the view."

SuperClass

同理。这只是一个指南。你永远不能说 "a class can only have N members at most"。此建议对任何 N 都是错误的,因为 N 是上下文敏感的。

组合优于继承!

很多时候开发人员会看 SOLID 之类的原则,而忘记基本的组合优于继承原则。

首先要记住的是,继承将您紧紧地绑定到基础 class。这导致诸如拒绝遗赠(违反 Liskov 替代原则)等问题。

当我们在 OOP 中谈论 classes 时,我们定义的是与数据相关的行为,而不是对象。 当我们根据行为对问题建模时,它是努力实现,我们可以得到小积木。

您可以将 MyAwesomeViewModel 中的核心行为定义为微小的 classes,您可以在其他 classes 中引用它。通过这种方式,您可以轻松组合 MyAwesomeViewModelWithBunchNewProperties.

等对象

关于Single responsibility principle,这是一个非常容易被误解的原则。 SRP 指出共同生活的行为应该一起改变。 这意味着一组相互依赖并且会一起改变的行为属于同一个 class。

根据您的具体情况,视图模型通常可能无法从组合或继承中获益。视图模型是数据传输对象(DTO),它们不捕获行为。这里的代码重复很容易overlooked\acceptable。如果代码重复在您的视图模型中造成问题,只需从其他 DTO 中组合它们