问题标签 [open-closed-principle]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
1 回答
144 浏览

design-patterns - 基于继承的 API 是否违反 OCP?这可以通过提供者模型/依赖注入来实现吗?

我是第一次设计 API,并尝试遵循 SOLID 指南。我发现自己挣扎的一件事是平衡 OCP 和可测试性与简单性和易于扩展性。

这个开源 API 面向科学建模和计算。其目的是让各个小组能够轻松地将他们的特定模型导入到这种“可插拔”架构中。因此,它作为一个项目的成功将取决于这些科学家在没有不必要的开销或过于陡峭的学习曲线的情况下传授他们特定领域知识的难易程度。

例如,我们的计算引擎依赖于“矢量化”计算——我们很少需要只计算一个标量值。许多模型可以利用这一点并执行“开销”计算以在每个标量子计算中重复使用。但是,我希望用户能够定义一个简单的标量操作,它将继承(或以其他方式提供)默认的矢量化行为。

我的目标是成功

1) 让新手用户尽可能简单地实现他们的基本计算模型 2) 让高级用户尽可能简单地覆盖矢量化行为

...当然,同时保持 SoC、可测试性等。

经过几次修改,我有了一些简单且面向对象的东西。计算合约是通过接口定义的,但鼓励用户从提供默认向量化的抽象 ComputationBase 类派生。这是设计的按比例缩小的表示:

对于 ComputationBase 中的向量化计算,我最初使用了提供者模式(通过构造函数 DI),但将标量计算保持为抽象。理由是这是很好的“受保护的变体”——基类将始终“拥有”向量化操作,但将计算委托给注入的提供程序。从可测试性和矢量化代码重用的角度来看,这似乎通常也是有益的。但是,我遇到了以下问题:

1) 标量(继承)和向量(提供者)计算方法的异质性似乎可能使用户感到困惑,要求似乎过于复杂,而且代码味道很差。

2) 为向量化创建一个“单独的”提供者是一个有漏洞的抽象——如果提供者要做任何聪明的事情,它通常需要类的实现的内部知识。我发现自己创建了私有嵌套类来实现它们,这告诉我这是一个无法分离的问题

这是 w/r/t OCP vs 可测试性 vs 简单性的好方法吗?其他人是如何设计他们的 API 以在各种复杂程度下进行扩展的?你会使用比我所包含的更多的依赖注入机制吗?我也对关于良好 API 设计的一般参考资料感兴趣,而不是对这个特定示例的回答。谢谢。

谢谢,大卫

0 投票
5 回答
9973 浏览

.net - 在 Bootstrapper 中配置 Automapper 违反了开闭原则?

我在 Bootstrapper 中配置 AutomapperBootstrap()Application_Start()Bootstrapper. .

你怎么看,我真的违反了这个原则吗?

0 投票
3 回答
3883 浏览

design-patterns - 工厂方法模式是否违反了开放/封闭原则?

工厂方法模式(不要与工厂或抽象工厂模式混淆)是否违反了开放/封闭原则

更新:为了澄清,我指的是具体类上有静态工厂方法的场景。例如(这来自 FMP 上的 Wikipedia 页面):

私有构造函数不会阻止类被子类化,即扩展吗?

难道不需要修改类来支持新的工厂方法吗?例如,如果该类最初只有 fromCartesian 而后来需要 fromPolar,那么是否必须修改该类以支持这一点?

这两个都不违反开/关吗?

0 投票
2 回答
1150 浏览

c# - 使用 IoC 和 Dependency Injection,如何在不违反 Open-Closed 原则的情况下,用新的实现层包装代码?

我试图弄清楚在实践中如何做到这一点,以免违反开放封闭原则。

假设我有一个名为 HttpFileDownloader 的类,它有一个函数,它接受一个 url 并下载一个将 html 作为字符串返回的文件。这个类实现了一个只有一个函数的 IFileDownloader 接口。因此,在我的整个代码中,我都引用了 IFileDownloader 接口,并且每当 IFileDownloader 被解析时,我的 IoC 容器都会返回一个 HttpFileDownloader 实例。

然后经过一段时间的使用,发现偶尔服务器太忙,抛出异常。我决定要解决这个问题,如果我遇到异常,我将自动重试 3 次,并在每次重试之间等待 5 秒。

所以我创建了 HttpFileDownloaderRetrier,它有一个函数在一个 for 循环中使用 HttpFileDownloader,最多 3 个循环,每个循环之间等待 5 秒。为了测试 HttpFileDownloadRetrier 的“重试”和“等待”能力,我通过让 HttpFileDownloaderRetrier 构造函数采用 IFileDownloader 来注入 HttpFileDownloader 依赖项。

所以现在我希望 IFileDownloader 的所有 Resolving 都返回 HttpFileDownloaderRetrier。但是如果我这样做,那么 HttpFileDownloadRetrier 的 IFileDownloader 依赖项将获得一个自身的实例,而不是 HttpFileDownloader 的一个实例。

所以我可以看到我可以为 HttpFileDownloader 创建一个名为 IFileDownloaderNoRetry 的新接口,并更改 HttpFileDownloader 来实现它。但这意味着我正在更改违反 Open Closed 的 HttpFileDownloader。

或者我可以为 HttpFileDownloaderRetrier 实现一个名为 IFileDownloaderRetrier 的新接口,然后将我所有的其他代码更改为引用该接口而不是 IFileDownloader。但是,我现在在所有其他代码中都违反了 Open Closed。

那么我在这里错过了什么?如何在不更改现有代码的情况下用新的实现层(重试和等待)包装现有实现(下载)?

如果有帮助,这里有一些代码:

0 投票
1 回答
317 浏览

open-closed-principle - 开闭原则,重构

我正在尝试将 OCP 应用到我拥有的代码片段,在它的当前状态下真的很臭,但我觉得我并没有走到最后。

当前代码:

这真的很难看,我的新方法是这样的:

当出现新的 SomeObject 类型时,我必须实现该特定对象的存储方式,这将破坏 OCP,因为我需要更改模型类。

将存储逻辑移动到 SomeObject 也感觉不对,因为我会违反 SRP(?),因为在这种情况下 SomeObject 几乎就像一个 DTO,它的责任不是如何知道如何存储自己。

如果出现了 SomeObject 的新实现,缺少谁的 store 实现,我会因为 Model 类的 Store 方法中的异常而出现运行时错误,这也感觉像是代码异味。

这是因为调用代码的形式为

我不会知道序列对象的具体类型。

我似乎无法掌握 OCP 的概念。任何人都有任何具体示例或链接,而不仅仅是一些汽车/水果示例?

0 投票
1 回答
157 浏览

c# - 使用 C# 在此应用程序中应用开闭原则的最合适方法是什么?

设想

每天晚上,我们都会对大约一百万份客户合同进行一系列计算。每份合同都与一组大约十种产品相关,其中每一种产品都可能对要​​执行的计算集采用变化(尽管总体流程大致相同)。给定产品的所有合同将在任何给定的夜晚使用相同的计算集;分布不均,从最小的几千到最大的几十万不等。

随着时间的推移,我们将添加新的类似(但不太可能相同)的产品,并且现有产品可能会通过对所采用的算法引入变化来进行调整。(顺便说一下,我希望算法能够在不修改代码的情况下进行参数化)。

我们用来支持这一点的核心“引擎”应该适用于产品开发和生产。后者由我们的运营人员管理,他们对他们接受的内容以及一般如何管理变更非常谨慎。

在考虑架构时,我强烈地被开闭原则所吸引:

软件实体应该对扩展开放,对修改关闭。

由于我们计划在 C# 中实现,我正在考虑规定函数在内核外部定义,以文本形式加载并在产品定义的当前版本控制下的产品运行开始时编译. 这些组件中的每一个都将编译为一个类,该类实现在应用程序的已编译元素中定义的接口(或等效接口)。我乐观地认为,从操作的角度来看,这将被视为一个加分项,因为回归测试的范围大大减少了。

问题

这有意义吗?任何人都可以就潜在的陷阱提供任何明智的建议吗?

我是否重新发明了依赖注入,我是否最好通过每次提供一个新的 DLL 来提供定期更改?如果是这样,这会使我们的日常产品开发过程变得更糟吗?

或者我应该采用更敏捷的方法,端到端构建一个产品,然后依次添加其他产品,看看我重构时会出现什么架构?

如果你还在我身边,谢谢你的耐心...

0 投票
3 回答
496 浏览

open-closed-principle - 业务逻辑发生变化时如何尊重开闭原则?

我们正在对我们的系统进行一些重大更改,我想知道实施这些新业务逻辑规则的最佳方法,同时尊重 SOLID 原则:

开放/封闭原则说“开放以进行扩展,但关闭以进行修改”好的,但我该如何进行修改?我的意思是,我不想保留旧的业务逻辑,而在我看来“扩展”主要是指“添加代码”而不是“更改代码”,那么我理解错了什么?

0 投票
3 回答
1122 浏览

c# - 抽象方法和开闭原则

假设我有以下人为的代码:

如果我只看这个Level2类,我可以立即看到它Level2.PrintHierarchy遵循打开/关闭原则,因为它自己做一些事情并调用它覆盖的基本方法。

但是,如果我只看这个Level1类,它似乎违反了 OCP,因为它不调用base.PrintHierarchy——事实上,在 C# 中,编译器通过错误“无法调用抽象基成员”来禁止它。

使Level1看起来遵循 OCP 的唯一方法是更改Root.PrintHierarchy​​为空的虚拟方法,但是我不能再依赖编译器来强制派生类来实现PrintHierarchy.

我在这里维护代码时遇到的真正问题是看到几十个override方法不调用base.Whatever(). 如果base.Whatever是抽象的,那么很好,但如果不是,那么该Whatever方法可能是被拉入接口而不是具体的可覆盖方法的候选方法——或者需要以其他方式重构类或方法,但要么方式,它清楚地表明设计不佳。

如果没有记住Root.PrintHierarchy抽象的内容或在其中添加评论Level1.PrintHierarchy,我是否还有其他选择可以快速确定形成的类Level1是否违反 OCP?


评论中有很多很好的讨论,也有一些很好的答案。我很难弄清楚到底要在这里问什么。我认为让我感到沮丧的是,正如@Jon Hanna 指出的那样,有时虚拟方法只是表示“你必须实现我”,而其他时候它意味着“你必须扩展我——如果你没有调用基本版本,你破坏我的设计!” 但是 C# 没有提供任何方法来指示您的意思,除了抽象或接口显然是“必须实现”的情况。(除非代码合同中有一些东西,我认为这有点超出了范围)。

但是如果一种语言确实有一个必须实现与必须扩展的装饰器,如果它不能被禁用,它可能会给单元测试带来巨大的问题。有没有类似的语言?这听起来很像按合同设计,所以如果它在埃菲尔,例如,我不会感到惊讶。

最终结果可能就像@Jordão 所说的那样,而且完全是上下文相关的;但在我接受任何答案之前,我将让讨论开放一段时间。

0 投票
1 回答
173 浏览

oop - 如何编写符合 OCP 的代码?

我最近一直在尝试学习基本的设计原则,而 OCP 让我有些困惑。有意义的是,当发生更改时,最好扩展系统而不是修改现有的和工作的部分。但这不是更多关于如何在系统中实现更改而不是如何设计的原则吗?通过使用子类化,不是所有代码基本上都可以扩展吗?以及如何关闭任何代码以进行修改 - 这不仅取决于实施更改的人选择使用它的方式吗?

也许一些不遵循 OCP 的代码示例以及它究竟是如何违反该原则的,这对我理解这一点最有帮助。

谢谢

0 投票
1 回答
1873 浏览

oop - 使用 BDD 时遵循开放/封闭原则有什么好处吗?

开放/封闭原则似乎是关于防止对象或方法的回归。鉴于您的代码已被测试覆盖,因为您正在练习 BDD,这似乎是一个多余的要求。此外,它似乎通过在 API 级别而不是语言级别要求可扩展性来引入额外的复杂性。