Answers:
这可能是最难解释的可靠原则。让我试试。想象一下,您编写了一个Invoice类,该类可以完美运行并且没有错误。生成发票PDF。
然后有人说他们想要带有链接的HTML发票。您无需更改发票中的任何代码即可满足此要求。相反,您可以创建另一个类,即HTMLInvoice,该类可以完成他们现在想要的。利用继承,您不必在HTMLInvoice中编写很多重复的代码。
使用旧发票的旧代码不会被破坏或以任何方式受到真正影响。新代码可以使用HTMLInvoice。(如果您也执行Liskov Substitutability,即L,您可以将HTMLInvoice实例提供给期望有Invoice实例的现有代码。)每个人都过着幸福的生活。
发票不可修改,可扩展。而且,您必须提前正确编写发票才能使用,顺便说一句。
您是否阅读过ObjectMentor的Bob叔叔的朋友们的“开放原则”文章?我认为这是最好的解释之一。
有许多与面向对象设计相关的试探法。例如,“所有成员变量应为私有”,或“应避免使用全局变量”,或“使用运行时类型标识(RTTI)是危险的”。这些启发式的来源是什么?是什么使它们真实?他们总是对的吗?本专栏将探讨作为这些启发式方法基础的设计原理-开闭原理。
正如Ivar Jacobson所说:“所有系统在生命周期中都会发生变化。在开发预期比第一个版本更长时间使用的系统时,必须牢记这一点。早在1988年,贝特兰德·迈耶(Bertrand Meyer)就提出了当今著名的开闭原理,为我们提供了指导。释述他:
应该打开软件实体(类,模块,功能等),但要对其进行修改则关闭。
当对程序的单个更改导致对从属模块的级联更改时,该程序将显示出我们与“不良”设计相关联的不良属性。该程序变得脆弱,僵化,不可预测和不可重用。开闭原理以非常直接的方式对此进行了攻击。它说您应该设计永远不变的模块。当需求改变时,您可以通过添加新代码而不是通过更改已经起作用的旧代码来扩展此类模块的行为。
描述
符合开闭原理的模块具有两个主要属性。
- 它们是“开放扩展”。
这意味着可以扩展模块的行为。我们可以使模块随着应用程序需求的变化或满足新应用程序的需求而以新的和不同的方式运行。- 它们是“封闭修改”。
这样的模块的源代码不正确。不允许任何人对其进行源代码更改。似乎这两个属性相互矛盾。扩展模块行为的正常方法是对模块进行更改。通常认为无法更改的模块具有固定的行为。如何解决这两个相反的属性?
抽象是关键...
凯特·格雷戈里(Kate Gregory)的回答非常好,但考虑另一种情况,即通过对现有Invoice
班级进行较小的更改就可以满足新的要求。例如,假设必须在发票PDF中添加一个新字段。根据OCP,即使可以通过更改几行代码在现有实现中添加新字段,我们仍然应该创建一个新的子类。
以我的理解,OCP反映了80年代和90年代初的现实,那里的项目通常甚至不使用版本控制,更不用说进行自动回归测试或使用复杂的重构工具了。OCP试图避免破坏手动测试并投入生产的代码的风险。如今,我们有更好的方法来管理破坏可用软件(即版本控制系统,TDD和自动化测试以及重构工具)的风险。