虽然我在 ObjC 中编码,但这个问题是有意与语言无关的 - 它应该适用于大多数 OO 语言
假设我有一个“Collection”类,我想创建一个继承自“Collection”的“FilteredCollection”。过滤器将在对象创建时设置,从它们开始,该类的行为就像一个“集合”,过滤器应用于其内容。
我以明显的方式和子类 Collection 做事。我覆盖了所有的访问器,并认为我已经完成了一项非常出色的工作——我的 FilteredCollection 看起来应该像一个集合一样,但其中的对象与我的过滤器相对应的对象被过滤给用户。我想我可以愉快地创建 FilteredCollections 并将它们作为 Collections 在我的程序中传递。
但我来测试 - 哦不 - 它不起作用。深入调试器,我发现这是因为某些方法的 Collection 实现正在调用重写的 FilteredCollection 方法(例如,Collection 在迭代其对象时依赖于“count”方法,但现在它正在获取过滤后的计数,因为我覆盖了 count 方法以提供正确的外部行为)。
这里有什么问题?为什么感觉有些重要原则被违反了,尽管它也感觉像 OO '应该' 以这种方式工作?这个问题的一般解决方案是什么?有吗?
顺便说一句,我知道这个问题的一个很好的“解决方案”是在我将它们放入集合之前过滤对象,并且根本不需要更改 Collection,但我问的是一个更普遍的问题比这 - 这只是一个例子。更普遍的问题是不透明父类(super class)中的方法依赖于可能被子类更改的其他方法的行为,以及在您想要子类化对象以更改此类行为的情况下该怎么做。
最佳答案
Collection
你继承的有一定的契约。类的用户(包括类本身,因为它可以调用自己的方法)假设子类遵守契约。如果幸运的话,契约(Contract)会在其文档中明确无误地指定...
例如,合约可以说:“如果我添加一个元素 x
,然后遍历集合,我应该得到 x
返回”。看来你的FilteredCollection
实现违反了该契约(Contract)。
这里还有一个问题:Collection
应该是一个接口(interface),而不是一个具体的实现。一个实现(例如 TreeSet
)应该实现那个接口(interface),当然也遵守它的契约。
在这种情况下,我认为正确的设计是不继承 Collection
,而是创建 FilteredCollection
作为围绕它的“包装器”。大概FilteredCollection
不应执行 Collection
接口(interface),因为它不遵守集合的通常约定。
关于language-agnostic - 子类导致父类(super class)中的意外行为——OO 设计问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1487194/