16

这个问题与 OCP 是什么无关。我也不是在寻找简单的答案。

所以,这就是我问这个的原因。OCP 在 80 年代后期首次被描述。它反映了当时的思想和背景。令人担忧的是,在代码已经测试并投入生产之后,更改源代码以添加或修改功能最终会带来太大的风险和成本。所以想法是尽可能避免更改现有的源文件,只以子类(扩展)的形式添加到代码库中。

我可能错了,但我的印象是基于网络的版本控制系统(VCS)当时并没有被广泛使用。关键是 VCS 对于管理源代码更改至关重要。

重构的想法是最近才出现的。支持自动重构操作的复杂 IDE 在当时肯定是不存在的。即使在今天,许多开发人员也没有使用可用的最佳重构工具。这里的重点是,这些现代工具允许开发人员在几秒钟内安全地更改数千行代码。

最后,今天自动化开发人员测试(单元/集成测试)的想法很普遍。有许多免费和复杂的工具支持它。但是,如果我们从不/很少更改现有代码,那么创建和维护大型自动化测试套件有什么好处呢?正如 OCP 所要求的,新代码只需要新的测试。

那么,OCP 在今天真的有意义吗?我不这么认为。相反,如果新功能不需要新类,我确实更愿意在添加新功能时更改现有代码。这样做将使代码库更简单、更小,并且更容易阅读和理解。破坏先前功能的风险将通过 VCS、重构工具和自动化测试套件进行管理。

4

6 回答 6

9

当您不是代码的使用者时,OCP 很有意义。如果我正在编写一个类,并且我或我的团队正在编写所有使用它的类,我同意。随着事情的变化进行重构根本不是什么大不了的事。

另一方面,如果我正在为我的客户编写 API,或者我在一个大型组织中有多个不同兴趣的消费者,那么 OCP 至关重要,因为我不能那么容易地重构。

此外,如果你只是重构你的类以满足每个人的需求,那么你会得到一个臃肿的类。如果你设计的类允许消费者扩展你的类而不是修改它,你就不会有这个问题。

于 2009-09-12T23:59:51.433 回答
6

我从来没有听说过OCP是这样的。也许你指的是别的东西,但我知道的 OCP 说“一个模块/类必须对扩展开放,但对修改关闭,这意味着你不应该修改模块的源代码来增强它,但是模块或对象应该易于扩展。

想想 eclipse(或任何其他基于插件的软件)。您没有源代码,但任何人都可以编写插件来扩展行为或添加其他功能。你没有修改eclipse,但是你扩展了它。

所以,是的,Open/Closed 原则确实非常有效,而且是个好主意。

更新:

我看到这里的主要冲突是仍在开发中的代码和已经被某人发布和使用的代码之间。所以我去和这个原理的作者Bertrand Meyer核实了一下。他说:

如果一个模块可供其他模块使用,则称该模块已关闭。这假设该模块已被赋予定义良好、稳定的描述(其接口在信息隐藏的意义上)。在实现级别,模块的闭包还意味着您可以编译它,也许将其存储在库中,并使其可供其他人(其客户)使用

因此,事实上,开放/封闭原则仅指稳定、可编译和使用的实体。

于 2009-09-13T01:38:46.383 回答
3

好的,这是我的回应。

我无法证明这一原则的历史渊源,但它在现代仍然经常被援引。我不认为改变功能代码是危险的(尽管它当然是)它允许你分离想法。

假设我们有一个组件

public class KnownFriendsFilter{
  IList<People> _friends;
  public KnownFriendsFilter(IList<People> friends) {
    _friends = friends;
  }
  public IList<Person> GetFriends(IList<Person> people) {
    return people.Where(p=>_friends.Contains(p)).ToList();
  }
}

现在说这个特定组件需要稍微修改的算法 - 例如,您要确保传入的初始列表包含不同的人。这将是 KnownFriendsFilter 关注的问题,因此请务必更改类。

然而,这个类和它支持的特性是有区别的。

  • 这个类实际上只是为已知朋友过滤一组人
  • 它支持的功能是从一组人中找到所有朋友

区别在于特性与功能有关,而类与实现有关。我们更改该功能的大多数请求将超出该类的具体职责。

例如,假设我们要添加以字母“X”开头的任何名称的黑名单(因为这些人显然是太空人,而不是我们的朋友)。那是支持该功能的东西,但实际上并不是该课程的全部内容,将其粘在课程中会很尴尬。当下一个请求出现时,现在该应用程序仅由厌恶女性者使用,并且任何女性名字也必须排除在外,该怎么办?现在你必须添加逻辑来决定班级中的名字是男性还是女性 - 或者至少知道其他一些知道如何做到这一点的班级 - 班级的责任越来越大并且变得非常臃肿!那么横切关注点呢?现在我们想在过滤一组人时记录,那也可以吗?

最好分解出一个 IFriendsFilter 接口并将这个类包装在一个装饰器中,或者将它重新实现为 IList 上的责任链。这样,您就可以将这些职责中的每一个放入支持该问题的自己的类中。如果您注入依赖项,那么任何使用此类(以及在我们的应用程序中集中使用)的代码都不需要更改!

再说一遍,原则不是永远不要更改现有代码 - 它是关于不要最终陷入这样一种情况,即您需要在使常用类的职责膨胀或必须编辑使用它的每个位置之间做出决定。

于 2009-09-13T00:14:16.997 回答
1

那么,OCP 在今天真的有意义吗?我不这么认为。

有时它会:

  • 当您向客户发布了基类并且无法在所有客户的机器上轻松修改它时(参见例如“DLL Hell”)

  • 当您是客户时,您自己没有编写基类,也不是维护它的人

  • 更一般地,基类被多个团队和/或多个项目使用的任何情况

另见康威定律

于 2009-09-13T00:03:51.380 回答
1

有趣的问题,基于开放封闭原则的严格定义,我可以看到你来自哪里。

我对开闭原则的定义略有不同,我认为这个原则应该适用,那就是更广泛地应用它。

我想说,我在应用程序中涉及的所有类(作为一个整体)都应该关闭以进行修改并打开以进行扩展。所以原则是,如果我需要改变应用程序的行为和/或操作,我实际上并没有修改一个类,而是添加一个新类,然后更改关系以指向这个新类(当然取决于大小的变化)。如果我遵循单一职责并利用控制反转,则应该发生这种情况。然后发生的是所有的变化都变成了扩展。他们的系统现在可以以以前的方式和新的方式行动,并且在它们之间改变=改变关系。

于 2009-09-13T00:04:16.357 回答
0

这里的重点是,这样的现代工具允许开发人员在几秒钟内安全地更改数千行代码。

如果您有“a”开发人员,那很好。如果你在团队中工作,当然有版本控制,可能有分支和合并,那么确保来自不同人的更改往往最终集中在不同文件中的能力对于能够控制正在发生的事情非常重要。

您可以想象特定语言的合并/分支工具可以并行完成三个重构,并像更改隔离文件一样轻松地合并它们。但是这样的工具不存在,如果有,我也不想依赖它们。

于 2009-09-13T00:09:18.983 回答