一个班级应该知道它的子类吗?


24

一个班级应该知道它的子类吗?一个类是否应该执行特定于给定子类的操作?

我的直觉告诉我,这是一个糟糕的设计,似乎是某种反模式。


6
通常不会,但是在特殊情况下很有用。
CodesInChaos

一种反模式,一种众所周知的模式:“ Liskov替代原理”,有时也称为LSP,请在en.wikipedia.org/wiki/Liskov_substitution_principle中进行
Jimmy Hoffa 2013年

5
@JimmyHoffa-“ Liskov替代原理”不是反模式的名称。反模式可能会违反LSP,但甚至不能肯定这在这里是对的。即使一个类型知道它是子类型,也有可能仍然可以用协方差和协方差代替这些子类型的父代。
马修·弗林

4
@MatthewFlynn也许我稍微松散地使用了LSP,但是在我的定义中,并不是所有的人都符合LSP的彼此要求,而是要求它们具有去耦关系,这样不仅它们彼此同意,而且您可以实现另一个不违反LSP的子类。如果层次结构是自觉的,则在不更改基类的情况下,外部实现可能无法满足LSP。也许这并不是严格意义上的LSP,但可能非常接近。是的,我应该说违反 LSP 是一种反模式
Jimmy Hoffa

2
几次遇到这种情况,我都将这些知识重构为可以在子类中覆盖的方法(通过将其抽象化或纯虚拟化,或者提供可以覆盖的明智默认实现来强制执行)。

Answers:


32

类的概念所隐含的答案是“否”。

无论您要处理的动作,数据或关系是所有子类的一部分,那么都应在超类中对其进行处理而不检查实际类型。或它仅适用于某些子类-那么您必须执行运行时类型检查才能做正确的事,只要其他人继承了父类,就必须更改超类(否则它可能会默默地做错事情),派生类中的更改可能会破坏未更改的超类等。

简而言之,您会遇到许多不良后果,这些后果通常足以使您无法立即拒绝此类解决方案。如果您的几个子类都做同样的事情,并且希望避免代码重复(实际上总是一件好事),那么更好的解决方案是引入一个中级类,所有这些子类都可以从中继承代码。


4
此规则的一个例外:类的文档应列出其自己的库本地的子类。
Kieveli 2013年

3
@Kieveli ...只要该文档保持最新状态。
mouviciel 2013年

14
任何可用的文档编制工具都应该能够绘制继承树...
johannes

13
我不认为这是例外。全班还是一无所知。该文档知道。
Honza Brabec

4
@Kieveli恩,我完全不同意。
吉米·霍法2013年

16

不仅应该是不知道,这根本不可能!通常,可以随时随地扩展类。它可以由编写时甚至不存在的类扩展。

一些语言允许扩展类由超类控制。在Scala中,一个类可以标记为sealed,这意味着它只能由同一编译单元(源文件)中的其他类扩展。但是,除非这些子类也为 sealed或,否则其他类final可以进一步扩展这些子类。

在Scala中,这用于建模封闭代数数据类型,因此规范的Haskell List类型:

data List a = Nil | Cons a (List a)

可以像这样在Scala中建模:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

并且您可以保证存在这两个子“类”,因为Listis sealed不能在文件外部扩展,Consis final根本不能扩展并且Nilis object不能扩展。

但这是一个特定的用例(通过继承建模代数数据类型),即使在这种情况下,超类实际上也不知道其子类。对于这种类型的用户而言,这更加保证了List,如果他对Niland 进行区分大小写,那么他的身后Cons将不会弹出任何其他选择。


在多种语言中起作用的是添加某种形式的abstract internal成员。
CodesInChaos


1

是的,有时。例如,当存在有限数量的子类时。甲访问者模式是这种方法的有用性的图示。

示例:某些定义明确的语法的抽象语法树(AST)节点都可以从Node实现访问者模式以处理所有节点类型的单个类中继承。


9
不,不,不。这是没有用的,永远不是一个好的解决方案。
苏珊(Sulthan)2013年

@Sulthan我曾在课堂外与AST一起工作过,我相信lorus不能真正理解这个问题。访客不是AST上的一种节点,它是一个单独的对象。它可以并且应该知道语法对象。但是高级AST节点不应该。

1
@JohnGaughan“知道”一词使我感到困惑。Node当然,基类不包含对其子类的任何直接引用。但是它包含accept带有Visitor参数的方法。并且每个子类都Visitor包含一个visit方法。因此,尽管Node没有直接引用其子类,但它通过Visitor接口间接“了解”它们。他们都通过它耦合在一起。
lorus

1
@Sulthan永不言败。选项类型是当超类知道后代一时的有效示例。但正如Jorg所说,它将被标记为已密封。
Pavel Voronin

然后达成一致:访问者需要知道它正在访问什么。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.