【问题标题】:Open closed principle开闭原则
【发布时间】:2017-12-01 12:06:05
【问题描述】:

我理解这个原则表明一个模块对扩展开放但对修改关闭,当我们想要升级/修改某个类中的方法时,这一点很清楚——我们创建另一个继承基类的类,然后覆盖此处的基本方法是为了保留这两个功能。

但是当我们想向基类添加另一个不同的方法时,情况如何呢?这是否被视为对基类的修改或扩展 - 可以将方法添加到基类,还是我们也应该创建继承基类的新类,然后将新方法放在那里?

另外,向基类添加属性和字段的问题也是一样的。

【问题讨论】:

  • 是否可以选择修改界面?并回答您的问题 - 我将其视为更改而不是扩展。至于方法,您是否考虑过扩展方法,根据定义是扩展原始功能?
  • 开放封闭原则的真正含义是,当您想为流程添加更多功能时,您不必更改原始流程。
  • @Konrad Viltersten 我可能是错的,但我认为只有当你想扩展你不拥有的类的功能时才首选扩展方法。
  • 我对OCP的理解如下:你的系统应该被设计成如果你想扩展它,你编写新代码而不是重写旧代码
  • 我不同意那些声称这个问题是基于意见的投票结果。我们正在讨论一个完善的 SOLID 原则,以及某个行为是否违反了这一原则。这不是意见。

标签: c# open-closed-principle


【解决方案1】:

TL;DR
我认为添加新方法本质上意味着您不能违反 OCP;因为 OCP 主要关注对现有方法的鲁莽覆盖


示例:
基类CalculatorMyCustomCalculator 继承。基类只有两个数字的加减乘除方法。
MyCustomCalculator 中创建一个新方法,例如CalculateAverage(params int[]) 不违反 OCP。它不会修改任何现有方法背后的逻辑,它只是扩展计算器对象提供的方法。

新方法本质上不会改变旧方法。因此,它们不会修改现有逻辑;然后简单地扩展可用的内容。

所以不,它(本质上)不违反 OCP。

它几乎不可能违反 OCP,因为创建新事物(本质上)与修改现有事物完全不同。


需要注意的一些事项:

  • 我可以想到一个(有争议的)例外:重载。如果您创建了一个重载现有方法的新方法,那么您有责任遵守重载规则。最值得注意的是,这意味着重载的方法应该执行完全相同的任务(重载的方法只是基于不同的输入参数,但它们的处理和输出在功能上应该是等效的)。虽然我不能 100% 确定这是违反 OCP(因为它错误地扩展了基类)还是 SRP(因为它对重载方法的处理/输出产生了模棱两可的责任)。似乎两者兼而有之。
  • 即使您的新方法不会重载现有方法,同样的原则也适用。新方法是否与基类的职责(意图/目的)足够相关?因为如果它非常不同,那么您可能违反了SRP

编辑

我错过了你的部分问题

是否可以将方法添加到基类,或者我们也应该创建继承基类的新类,然后将新方法放在那里?

这取决于上下文。对于我提供的Calculator / MyCustomCalculator / CalculateAverage() 示例;我会本能地将它添加到基类中,因为计算平均值是与计算器职责相关的基本操作。

但是,如果我们正在讨论添加一个仅适用于复数的方法 (MethodForComplexNumbers()),那么我建议创建一个继承自 Calculator 并实现 MethodForComplexNumbers()ComplexNumberCalculator

但是,我认为这主要是由于 SRP,而不是 OCP

【讨论】:

  • 可能值得讨论一些极端情况:例如,如果一个类涉及任何类型的(反)序列化,那么添加到该类就足以破坏现有的序列化数据。
  • @grek40:你确定吗?因为数据的反序列化应该只真正尝试设置它在序列化数据中找到的属性。只要这些属性仍然存在于类中,它就不应该关心您添加的其他属性。特别是因为序列化需要一个无参数的构造函数,所以任何没有被设置的属性都应该已经有一个可行的默认值。 (此外,一般来说,SOLID 在边缘情况下往往会崩溃。这是一个很好的指导方针,但不是一个好的全球法律)。
  • @grek40:我看到的唯一问题是序列化 updated 类,然后尝试使用 outdated 版本对其进行反序列化。但这是一个本质上无法避免的问题,除非有一些过度的 try/catch 逻辑(我不支持)
  • 当然,一个有礼貌的序列化程序只会忽略额外的数据......但即使 C# 比其他语言少这些陷阱,仍然存在诸如二进制格式化程序之类的东西,requiring you to fulfill their special needs in order to not break things。如前所述,这是一个极端情况,而不是主要使用场景。另一个更常见的例子:考虑一个带有自动生成列的 UI 表 - UI 将通过使用附加属性扩展数据项类来改变。
  • 任何更改都是与可能与反射结合出现的可能副作用的权衡 - 最终开发人员必须决定这些事情是否是真正关心的问题手头的案子。没有任何限制地声明扩展不是关键的一般规则是无效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2016-09-12
  • 2010-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多