gpt4 book ai didi

oop - 脆弱的基类是 "inheritance breaks encapsulation"的唯一原因吗?

转载 作者:行者123 更新时间:2023-12-04 19:27:45 26 4
gpt4 key购买 nike

正如四人帮在“Design Patterns”中所说:“it's often said that 'inheritance breaks encapsulation'”,在“面向对象编程语言中的封装和继承”中解释了斯奈德。

然而,每次我读到“inheritance breaks encapsulation”时,这种说法背后的原因要么是含糊不清的解释,
或者用 Fragile Base Class 的例子来解释问题。

在阅读论文时,我有一种感觉,真正打破封装的继承的唯一属性是向下调用,这是 open recursion 允许的特性。 ( dynamic dispatch on this )并定义为“当父类(super class)方法调用在子类中被覆盖的方法时”,根据 Ruby & Leavens 在“安全创建正确的子类而不看父类(super class)代码”中的说法。
此外,开放递归显然是 Fragile Base Class 的原因问题,根据奥尔德里奇在“选择性开放递归:脆弱基类问题的解决方案”中的说法。

因此,如果脆弱基类问题是“继承破坏封装”的唯一原因,那么说 会更清楚。向下调用破坏封装 .由于一些解决方案可以在仍然使用继承的同时避免向下调用,因此继承本身并没有真正涉及破坏封装。此外,四人帮提出的摆脱继承的委托(delegate)模式也可以允许开放递归和向下调用,因为委托(delegate)人的上下文(this)被委托(delegate)人使用(这会导致一种脆弱的委托(delegate)类问题)。

因此我的问题是:
脆弱的基类问题是说“继承破坏封装”的唯一原因吗?

最佳答案

你的问题很有趣。我倾向于同意你的看法:继承的主要问题是父级和子级之间的耦合。这种关系阻碍了父类在不破坏其子类的情况下进化的能力。

我的意思是你想问脆弱基类问题是否是违反“继承破坏封装”原则的唯一表现,对吗?

TLDR;

我相信(和你一样),如果我们在使用继承时打破封装,那么,这个强耦合问题的表现肯定会在父类的脆弱性上很明显,当它改变时会破坏它的子类。

所以,在这个意义上或解释上,我认为你可能是对的。

相反的情况不一定正确,即拥有脆弱的基类并不一定意味着您违反了继承-封装规则。

耦合是问题

我仔细阅读了你提供的同一个引用书目,这段摘录可以对这个问题有所了解。

来自设计模式:可重用面向对象软件的元素:

“ parent classes often define at least part of their subclasses’ physical representation. Because inheritance exposes a subclass to details of its parent’s implementation, it’s often said that “inheritance breaks encapsulation” [Sny86].”



因此,似乎 GoF 在这里暗示了基本问题是耦合问题。显然,脆弱的基类是耦合问题的表现,所以你可能仍然在做一些事情

一个对象通常有一个公共(public)接口(interface),向世界公开它可以做什么的契约。对象的公共(public)接口(interface)中的这​​些方法必须满足对象提供的服务的几个先决条件、不变量和后置条件。因此,对象的用户根据该契约(Contract)与其进行交互。

对象的用户不需要知道合约的实现细节。因此,如果我们违反该契约(Contract),该对象的所有用户都会受到影响。

这种对对象公共(public)接口(interface)的依赖是一种耦合形式,当存在耦合时,就会有一种脆弱性可以改变。

例如,司机不需要知道液压方向盘系统在他们的汽车中是如何工作的,但他们仍然可以驾驶轿车或 SUV 就好像它们是一样的东西,因为他们了解方向盘和契约(Contract)的抽象管理其公共(public)接口(interface)。然而,如果我们改变方向盘,一般来说,就像叉车一样工作,那么可能每个人都会撞车(我们破坏了方向盘公共(public)界面)。

因此,预计类的公共(public)接口(interface)非常稳定,并且预计那里的任何更改肯定会破坏其客户端的功能。

当子类不能仅仅依赖于对象的公共(public)接口(interface)时,当它们需要额外了解其父类如何实现以提供功能性子实现时,继承会破坏封装(这就是为什么在某些情况下委托(delegate)是一个很好的选择,因为委托(delegate)只取决于对象的公共(public)接口(interface))。

状态耦合

这以不同的方式表现出来,例如,如果您可以直接访问父类中的状态变量,则可能会破坏封装(在您分享的 Snyder 的文章中也提到过)。

从面向对象编程语言中的封装和继承:

"To preserve the full benefits of encapsulation, the external interfaces of a class definition should not include instance variables"



在带有可访问性修饰符的静态语言(例如 Java 或 C#)中,如果我们从父类公开非私有(private)字段,则可能会出现这种违规行为。在没有可访问性修饰符的动态语言中,当子类实现者访问仅用于父类私有(private)使用的字段(例如,Python 中的 _field)时,就会出现此类违规。

你会认为这个字段访问问题是脆弱基类问题的一部分吗?在我看来,这种形式的继承耦合不一定包含在您分享的著名文章的脆弱基类问题的想法中。

protected 接口(interface)耦合

这表明的另一种方式是现在父类可能需要公开一个新接口(interface)而不是公共(public)接口(interface),但仅用于继承目的: protected 接口(interface)。因此,它可能需要公开一组“ protected ”或“特权”方法,这些方法可以访问通常未在为常规对象用户提供的公共(public)接口(interface)中公开的其他详细信息。

这显然是必要的,因为子类需要这些额外的细节才能为父类提供一些扩展功能或改变行为的合理实现。

现在父类还需要确保这个 protected 接口(interface)非常稳定,因为那里的任何更改都会破坏继承类,就像其公共(public)接口(interface)的更改会破坏常规类用户一样。

在这一点上,我们遇到了一种强耦合形式,由于它可能会在其子类中引起潜在问题,这种耦合可能会阻止父类在 future 发展。

现在,注意耦合和封装的破坏在设计时就表现出来了,因此,基类的脆弱性也被引入这里,即使这种耦合问题从未在代码中表现出来,因为我们从未导致父类发生变化。

所以,我的解释是继承引入的耦合导致封装破坏,进而导致你描述的脆弱基类问题。

在某种程度上,您的问题似乎暗示了一个因果关系链,您似乎认为脆弱的基类问题是破坏继承的原因,但在我的情况下,我认为情况正好相反:父子之间的耦合破坏了封装和这种高耦合水平在设计中表现为脆弱的基类问题。

无封装破损的脆性

话虽如此,我们现在有一个问题,我们能否在不破坏封装的情况下拥有一个脆弱的基类?

我相信我们会。子类可能只完全依赖于父类的公共(public)接口(interface)。例如,子类可能提供了一个全新的方法,它没有被继承,也不是父类的公共(public)或 protected 接口(interface)的一部分。

然后有一天,我们在父类中添加了一个新方法,该方法与我们很久以前在子类中添加的签名完全相同,但有一个完全不同的意图。

现在我们打破了一些东西,因为这个对象的用户希望新方法的行为与父类接口(interface)中声明的一样,而不是在子类中实现的。

这个错误可能很难捕捉。在某些语言中,这可能会导致子类失败;在其他情况下,可以假设 child 覆盖的版本是使用权。

这个问题的一个变体是,如果父类中定义的新方法与子类中定义的完全相同的方法具有不同的可访问性修饰符,作为两个类的独立演变的一部分。

无论如何,这些差异不一定会破坏封装,但由于继承引入的耦合,它们确实使父/子关系变得脆弱,对吗?

换句话说,尽管在这种情况下父类和子类之间的封装很好,但父类是脆弱的。

用继承破坏封装确实会导致基类脆弱,但据我所知,脆弱的基类并不一定意味着继承关系中存在封装问题。

关于oop - 脆弱的基类是 "inheritance breaks encapsulation"的唯一原因吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51002008/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com