由于开闭原则处理继承层次
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 中组合它们
当我尝试遵循 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 类 然后实现的接口。为了避免您的继承层次结构,您可以更喜欢组合以
的方式构建您的 ViewModelSOLID 原则 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 中组合它们