继承打破封装? [关闭]

Posted

技术标签:

【中文标题】继承打破封装? [关闭]【英文标题】:Inheritance breaking encapsulation? [closed] 【发布时间】:2012-03-09 20:06:16 【问题描述】:

人们说继承破坏了封装,我同意这一点。他们说委托更好——尽管委托中的修饰符也可以是公共的/受保护的。

那么继承破坏封装的真正原因是因为来自超类的公共/受保护修饰符的“敲击”效应被暴露给任何扩展当前子类的新类吗?

【问题讨论】:

继承破坏封装?只有在被滥用时...继承是面向对象设计的一个组成部分。 继承不会破坏封装。派生类无权访问其基类的私有成员。 “knock-on”效应是 OOP 不可或缺的一部分;如果动物会呼吸,那么预计狗、猫、人类……也会呼吸。它们没有泄露动物的内在本质;才意识到。 谷歌搜索“继承与委托”和“继承与组合”会产生一些关于该主题的非常好的文章。 很遗憾,我们不同意您问题的前提,因此对我们来说是错误的。 @Jon:请注意,您反对继承的几乎所有论点也适用于封装。假设有人可能错误地创建了一个破坏封装的受保护接口(通过继承),你为什么假设公共接口(通过委托)不能破坏封装?你的解决方案和你的前提一样有缺陷。 【参考方案1】:

是的。由于它使派生类可以访问基类的成员(取决于哪种语言和哪种继承),因此可以说它破坏了封装。恕我直言,这仅适用于您坚持最严格的封装。恕我直言,可以合理地说您接受派生类作为基类的扩展,因此以某种方式相关,并没有真正破坏封装。 纯粹主义者会不同意这一点。

看看http://www.ccs.neu.edu/research/demeter/papers/context-journal/node17.html 并搜索“休息”以获得学术解释。

【讨论】:

继承不会破坏封装,即使在纯粹的意义上也是如此。如果这些成员被封装并且无法从派生类访问,那么它们就不会是公共/受保护的。 @MooingDuck 看看我回答中的链接。众所周知,继承打破了纯粹意义上的封装。 “如果允许派生类访问从基类继承的成员,则基类的更改也可能需要维护派生类。”如果允许实现更改通过接口泄漏,这是否适用于任何类的 any 消费者?我发现引用来源中的论点很弱。 派生类只有在被允许的情况下才能直接操作基类的数据(通过protected访问修饰符)。如果您错误地将私有字段标记为受保护,那么就是您的设计被破坏了。 @Douglas “继承破坏封装”的学术论点是关于拥有“受保护”成员的能力。能够这样做的想法,即使是有意的,也是 OOP 的纯粹主义者说继承破坏封装的原因。【参考方案2】:

这取决于我们如何设计我们的类。在设计一个类时,我们应该牢记Open-Closed 原则。当我们谈论封装时,我们正在谈论修改,当我们谈论继承时,我们正在谈论扩展我们的应用程序,那么作为设计者,我们应该选择我们应该防止修改的内容(在我们的类中使用私有修饰符),从而封装我们的类以及为将来扩展保留的类的开放方面。(受保护的成员)。(想想作为 .net 语言中的部分概念,每个类都可以分成不同的文件,因此其中一些可以由程序员扩展,而另一些则使用代码生成工具生成)

【讨论】:

但是超类的设计者不能知道每个可能的类可以扩展它吗?那么继承赋予的特权会被第 6 代/第 7 代子类滥用吗? 被虐待是什么意思?当我们谈论封装时,我们谈论的是可以被私有修饰符保护的类的状态。第一个设计者应该做的就是确保该类的任何用户(按对象或按类)都不可能使用行为使对象处于无效状态 所以把超类的所有属性都设为私有,让方法受到保护,日子快乐? 并防止孩子改变关键行为。 8 年后我将投票给正确答案(打开/关闭)【参考方案3】:

我认为从我的角度来看,如果在基类或超类中添加新方法,它会中断。如果AB 的子类,即使A 没有以任何方式修改,B 的更改也会破坏A。 (这称为涟漪效应)

示例:假设 A 通过首先验证每个方法中的输入参数来覆盖 B 中的所有方法(出于安全原因)。如果在B 中添加了新方法并且A 没有更新,则新继承的方法会引入安全漏洞。

为了摆脱继承的陷阱,赞成或使用“组合”而不是继承,简单的聚合而不是继承,下面是一个例子:

想象两个类,PersonEmployee。您可以在Employee 中编写Person 并将Person 功能的请求转发给组合类,而不是要求EmployeePerson 继承,所以我们仍然可以获得重用Person 类的好处。

注意事项:

Person 类可能是抽象类或

Person 类可能与 Employee 位于同一包内的 protected

【讨论】:

【参考方案4】:

想象:

class A 
    void foo()
        ...
        this.bar();
        ...
    
    void bar()
        ...
    


class B extends A 
    //override bar
    void bar()
        ...
    


class C 
    void bazz()
        B b = new B();
        // which bar would be called?
        B.foo();
    

正如您在bazz 方法中看到的,哪个bar 将被调用?第二个 B 类中的 bar 将被调用。但是,这里有什么问题?问题是 A 类中的 foo 方法对 B 类中 bar 方法的覆盖一无所知,那么您的不变量可能会被违反。因为 foo 可能期望 bar 方法的唯一行为是在自己的类中,而不是被覆盖。这个问题被称为脆弱的基类问题

【讨论】:

以上是关于继承打破封装? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C#没有无缘无故地打破循环[关闭]

C# 如何打破公共 Void [关闭]

打破业务逻辑和数据层似乎重叠的最佳设计? [关闭]

打破2组中1到n个数的排列[关闭]

为什么说Java中要慎重使用继承

2017-06-26