我看过许多文章,其中称继承打破了封装
http://igstan.ro/posts/2011-09-09-how-inheritance-violates-encapsulation.html
但我无法理解它背后的概念。在给定的例子中
任何人都可以解释一下组合是如何避免这个问题的
答案 0 :(得分:1)
在您链接到的文章示例中:
set.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
这会调用InstrumentedHashSet.addAll
。
InstrumentedHashSet.addAll adds 3 to the counter, then calls
HashSet.addAll`。
HashSet.addAll
三次调用this.add
来添加每个元素。
但this.add
实际上是对InstrumentedHashSet.add
的调用!
每次拨打InstrumentedHashSet.add
都会向计数器添加1并调用super.add
。
每次HashSet.add
调用都会将元素添加到集合中。
最终结果是我们已经向计数器添加了6,而不是你想象的3。
为了正确实现InstrumentedHashSet
,它需要知道HashSet.addAll
的实现方式,而不是addAll
中的计数器增量。但这种知识打破了封装。子类不应该需要知道它的超类是如何实现的。
任何人都可以解释一下组合是如何避免这个问题的
它可以避免它,因为当HashSet.addAll
调用this.add
时,对this.add
的这些调用不会调用InstrumentedHashSet.add
。他们直接拨打HashSet.add
。
事实上,如果HashSet.addAll
实施Set.addAll
合同,InstrumentedHashSet
如何实施合同并不重要。这里没有破坏封装。
答案 1 :(得分:1)
我看过许多文章,其中说继承中断 封装
http://igstan.ro/posts/2011-09-09-how-inheritance-violates-encapsulation.html
但我无法理解它背后的概念。在给定的 示例
任何人都可以解释一下组合是如何避免这个问题的
“break”这个词可能过于强大,但继承肯定至少会破坏封装。
封装的一个重要目标是将API的实现与其合同分开。换句话说,希望使用API的客户只需要了解其“道路规则”(即其合同),而无需了解其内部工作原理。这允许大型系统的模块分离并独立发展(只要合同保持不变)。
不幸的是,当一个类继承自另一个类时,子类通常至少依赖于基类的一些实现细节......这违反了封装原则。
有两种常见的方式可以发生这种情况:
如果子类使用从基类继承的方法 - 并且该方法调用另一个方法,该方法是导出的API的一部分(所谓的“自用”API方法)。为了避免错误,子类的作者需要了解基类对方法的自我使用。这是API的客户端不需要了解的实现细节。
受保护的成员。有时,子类无法在不访问父类的数据或行为的情况下实现函数。 Java提供protected
访问类型以允许子类访问成员。通过这种方式,子类非常直接地访问将封装在父类中的信息和方法。因此,子类依赖于父类的实现细节。
让实施细节漏出你的课程的缺点是正确使用它不再仅仅取决于API合同。每当实现发生变化时,子类都有可能会中断。