为什么(/ did)Bertrand Meyer认为子类化是扩展“封闭式”模块的唯一方法?


19

在Meyer的“ 面向对象的软件构造”(1988)中,他将开放/封闭原则定义如下:

  • 如果模块仍可扩展,则称其为打开状态。例如,应该可以向其包含的数据结构添加字段,或者向其执行的功能集添加新元素。
  • 如果某个模块可供其他模块使用,则将其称为已关闭。假设已为模块提供了良好定义的稳定描述(信息隐藏的界面)。

他接着说:

如果重新打开模块,则还必须重新打开其所有客户端以对其进行更新,因为它们依赖于旧版本。…[此问题]每当模块必须由新功能或数据元素扩展时触发,从而触发直接和间接客户端中的更改。...使用经典的设计和编程方法,无法编写打开和关闭的模块。

Meyer解决这个难题的方法是:永远不要通过修改现有的类来扩展库模块;而是编写一个新模块来对现有类进行子类化,并使新客户端依赖于该新模块。

现在,在1988年,我正在用Turbo Pascal和Blankenship Basic编写玩具(程序)程序,而我在21世纪的专业经验是使用JVM,CLR和动态语言,所以我不知道Meyer的意思。通过“经典的设计和编程方法”。

Meyer的一个具体示例说明了为什么必须重新打开客户端模块(枚举上的switch语句,现在有更多的成员,需要更多的情况)似乎很合理,但是他几乎没有断言每次向库添加功能时都会断言模块,您需要更新其所有客户端

是否有历史原因使该主张在1988年显得不言而喻?例如,向C静态库添加函数或数据结构是否更改了布局,以至于即使使用向后兼容的API,也必须重新编译客户端?还是Meyer真的只是在谈论一种用于强制API向后兼容的机制?


3
有趣的问题!我感到答案将与某种程度上与抽象数据类型和面向对象的数据抽象之间的根本差异有关,后者是模块化编程中的两种主要数据抽象机制(Betrand Meyer称之为“经典方法” ”和面向对象的编程(请阅读注释!)。
约尔格W¯¯米塔格

真奇怪 它似乎与现实公然矛盾(即使在1988年)。同样,他提倡的方法将导致模块的无用扩散。

@ dan1111:埃菲尔的继承方法包括但不限于多重继承的方法与C ++,Java,C#等不同,因此毫不奇怪。毕竟,他专门开发Eiffel是为了支持他对OO的观点。
约尔格W¯¯米塔格

Answers:


18

据我所知,这个问题已经由贝特朗·迈耶本人回答,答案是,这个说法不准确。使用经典的设计和编程方法,确实可以找到一种编写打开和关闭模块的方法。

要找到答案,您需要研究本书的第二版(九年后,1997年出版)。根据第二版的序言,它是

不是更新,而是彻底重做的结果。原始版本的任何段落都保持不变。(实际上,几乎没有一行。)

特别是,令您困惑的陈述已经消失了。“§3.3五项原则”中仍然有“ 开放-封闭原则”一章,“§14.7继承简介”中对该主题有更深入的讨论,但是第一版中的声明不再存在。

相反,这里的重点是在OO方法中如何比以前的方法更方便,更惯用,

得益于继承,OO开发人员可以采用比以前的方法更多的增量方法来进行软件开发……(第3.3节)

这种双重要求(开放和封闭)看起来像是一个难题,而经典的模块结构没有任何线索。但是继承可以解决这个问题。一个类是封闭的,因为它可以被编译,存储在库中,作为基线并由客户端类使用。但是它也是开放的,因为任何新类都可以将其用作父类,从而添加新功能并重新声明继承的功能。在此过程中,无需更改原始文档或打扰其客户...(第14.7节)

由于您似乎也想知道Meyer在这里所说的“经典方法”是什么,因此您可以在§4.7传统模块化结构中找到这些说明。本部分说明了这些意思是“例程库”和“包”(对于后者,作者说,该术语取自Ada,并提到了具有此功能的其他语言-CLU中的群集和Modula中的模块)。

如果您考虑一下,这些方法最初都不是旨在帮助编写遵循开闭原理的代码。这可能会导致作者做出一些过早的评估,后来在第二版中对其进行了更正。


至于是什么使作者在第一版和第二版之间改变了主意,我认为可以在书本中找到答案,即在F部分:在各种语言和环境中应用该方法”。在这一章中,作者讨论了如何在较旧的语言中使用面向对象的方法:

诸如Fortran之类的经典语言根本不是面向对象的,但是仍然必须使用它们的人...可能希望在这些旧方法的限制范围内应用尽可能多的面向对象的思想。

尤其是,Meyer在这一部分中详细解释了如何在C甚至是Fortran中实现继承(尽管有一些警告和限制,但仍然有一些限制)。

您会看到,这确实需要从第一版修订该声明。看来几乎不可能解释如何调和“与经典方法......没有办法”与它究竟现实的例子可以完成。


有趣的是,我绝对必须尝试掌握第二版,但是我仍然不清楚为什么非OO的“经典”库也不能在不影响其功能的情况下添加(至少某些种类的)功能客户。
大卫·摩尔

@DavidMoles的事情是,有可能,我的答案的最后一部分对此进行了解释,而迈耶本人也意识到了这一点(当他为第二版进行重新编写时),甚至给出了如何完成此事的示例。“至于是什么使作者改变了主意……”等
2015年

嗯 我看不到“该库的版本2取代了版本1,并向后兼容,并添加了以下功能……”,除了以最广泛的概念性方式使用之外,都没有将其作为“继承”。
大卫·摩尔

(对我来说,继承意味着版本1仍然存在,并被版本2调用。)
David Moles

@DavidMoles用版本2替换(例如,更改源代码并重新编译)将不符合“封闭修改的条件”,您可以在Wikipedia文章中简单地检查一下:“实体可以允许其行为得以扩展而无需修改其源代码...”
咬到
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.