Effective Java: 转发类的安全性

6
《Effective Java第三版》中的第18条建议:优先使用组合而非继承,描述了在使用继承为类添加行为时可能会遇到的问题:
继承的一个相关问题是,超类可能会在后续版本中增加新的方法,导致子类变得脆弱。比如程序依赖于元素插入某个集合时满足某个谓词以确保安全性。可以通过子类化该集合并重写每个能够添加元素的方法来保证满足谓词的条件再添加元素。这样做很好用,直到在超类的后续版本中添加了能够插入元素的新方法。一旦出现这种情况,就可以通过调用新方法(该方法未在子类中重写)来添加“非法”元素。
推荐的解决方案是:不要扩展现有的类,而是给你的新类一个私有字段,引用一个现有类的实例...新类中的每个实例方法都调用包含的现有类实例上对应的方法,并返回结果。这被称为转发,新类中的方法称为转发方法... 在现有类中添加新方法将不会影响新类...编写转发方法很繁琐,但针对每个接口只需编写可重用的转发类一次,而且已经可以为你提供转发类。例如,Guava为所有集合接口提供了转发类。
我的问题是:在转发类中也可能添加新方法,这样不会破坏子类的不变式吗?像Guava这样的外部库如何在转发类中加入新方法而不危及其客户端的完整性?
3个回答

4
假设的默契是,正在编写转发类,因此你可以控制是否向其中添加任何内容。这是使用组合而不是继承的常见方式。
Guava示例似乎是指Forwarding Decorators,它们明确设计为可被继承的。但它们只是帮助创建这些转发类的工具,而无需在接口中定义每个方法;它们明确地不会保护您免受将来可能需要覆盖的任何方法的影响:
请记住,默认情况下,所有方法直接转发到代理,因此重写ForwardingMap.put不会改变ForwardingMap.putAll的行为。请小心覆盖每个必须更改其行为的方法,并确保您装饰的集合符合其协定。
因此,如果我正确理解了所有这些,那么Guava并不是一个很好的例子。

2
风险是否仍然存在,即可能向转发类添加方法,从而破坏子类的不变量?组合是继承的一种替代方案,因此在使用组合时,不存在子类。如果向转发类添加新的公共方法(可能访问包含实例的方法),这意味着您希望使用这些方法。

1
不确定您的意思。转发类应该被子类化。 - shmosel
1
转发类是子类化的,但这并不是在私有字段中对类进行子类化。 - Andy Turner
@shmosel 转发类是代替对包含实例的类进行子类化的一种选择,它们将调用转发到这些实例。如果您正在对它们进行子类化,则不使用组合。 - Eran
这是替代子类化“包含”的类的一种选择,但并不是完全替代子类化。 - shmosel
2
@shmosel,问题在于您描述的问题发生在您向基类添加方法时。转发类就是基类。因此,如果您要向该类添加方法,则(可能)意图如此,并且因此您正在执行以满足该转发类所假定的所有不变量。 - Andy Turner
@AndyTurner 我不太明白。这跟给 ArrayList 添加一个方法有什么不同吗? - shmosel

0

因为您是转发类的所有者,只有您可以向其中添加新方法,从而维护不变量。


我不是所有者,Guava 是。 - shmosel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接