堆栈扩展LinkedList。是否违反了Liskov替代原则?


13

存在具有诸如add_first(),add_last(),add_after(),remove_first(),remove_last()和remove()之类的函数的LinkedList类。

现在有一个Stack类,提供了push(),pop(),peek()或top()等功能,并且为了实现这些方法,它扩展了LinkedList类方法。这是否违反了《里斯科夫替代原则》?

例如,考虑在add_after()情况下将节点添加到链接列表的第n个位置的情况。这可以在基类中完成,但不能在Stack类中完成。此处的后置条件是否已减弱,或者您是否修改add_after()方法以将其添加到堆栈顶部?

另外,如果不违反,这是不好的设计吗?以及如何使用LinkedList类实现Stack功能?


6
问题将类定义为自身列表的子类有什么不利之处?问一个稍微不同的问题,但大多数答案也适用于此:您最好创建一个以List作为私有成员的Stack,而不是从List继承。
阿蒙

7
是的,这是一个糟糕的设计,因为它会阻止您将堆栈的内部表示形式更改为除链表(例如数组)以外的其他形式。您还将公开堆栈通常不支持的操作。使用组合而不是继承。

2
你想扩展LinkedList吗?您可能想听某个Joshua Bloch(在设计Java集合框架中起主要作用)的建议,当他说“优先考虑继承LinkedList而不是继承”时-也许在您的堆栈类内部,但实际上没有扩展它?
corsiKa

1
我会说它不能满足Liskov替换原则,因为“堆栈是一种链表”不是一个正确的语句。“堆栈”实际上是一个接口(从概念上讲,不是Java构造)。堆栈可以通过链表实现。就像其他人说的那样,您将使用类型为private的私有数据成员,LinkedList该成员在后台进行了繁重的工作(组成)。这样,您的代码用户就不会在需要列表的地方偶然使用堆栈,反之亦然。
Jasmijn

1
似乎更明智的是相反的。如果我这样做的话,我可能会让一个链表类实现一个“堆栈”接口,因为它完全有能力这样做。否则,这是错误的方法,因为堆栈是一个概念,链接列表是一种可以充当堆栈的实现。

Answers:


31

现在有一个Stack类,提供了push(),pop(),peek()或top()等功能,并且为了实现这些方法,它扩展了LinkedList类方法。这是否违反了《里斯科夫替代原则》?

不可以。在子类型中添加方法是完全可以的。

例如,考虑在add_after()情况下将节点添加到链接列表的第n个位置的情况。这可以在基类中完成,但不能在Stack类中完成。

一个违反了LSP。LSP表示,子类型的实例必须可以替代超类型的实例,而无需更改程序的期望属性。如果子类型删除一个方法,则任何调用该方法的代码都将崩溃(或获取NoMethodError异常,或类似的内容)。显然,“不崩溃”是理想的属性。

此处的后置条件是否已减弱,或者您是否修改add_after()方法以将其添加到堆栈顶部?

add_after()以这种方式修改方法会违反历史记录规则(其中最重要的规则!),因此无助于解决违反LSP的问题。

以及如何使用LinkedList类实现Stack功能?

通过使用组成。

注意:我上面写的所有内容适用于混淆子类型和子类化的语言!LSP是关于子类型化,而不是子类化。在不混淆这两个语言,这将是完全可以接受的,使Stack的一个子类LinkedList,只要你不把它一亚型LinkedList


9
历史规则是什么?我无法在互联网上找到它。
Zapadlo

10
当操纵一个子类型的对象并通过超类型的方法对其进行观察时,必须不可能观察到一个历史,而该历史也无法用超类型的对象来观察。该规则是使LSP适用于非功能语言的规则。所有其他东西早在那之前就已经众所周知。前提条件和后置条件规则是对函数类型的协方差和反方差规则的重新表述。历史规则使所有这些都适用于可变数据。没有它,LSP仅对纯功能语言有用。
约尔格W¯¯米塔格

2
我认为Wikipedia文章中的解释相当合理:wikipedia.org/wiki/Liskov_substitution_principle但与往常一样,您应该真正阅读原始资料。
约尔格W¯¯米塔格

@JörgWMittag:尽管我认为类型在其合同保证书中包括将要观察或将不会观察到的“历史”是合理的,但我认为每个超型都不能产生每个观察序列的必要性这可能在子类型的对象上是可能的-仅是超类型记录了超类型的消费者可能会观察到此类序列的可能性。
超级猫

1

由于JörgW Mittag已经解决了所有问题,因此我在以下部分进行了详细说明:

此处的后置条件是否已减弱,或者您是否修改add_after()方法以将其添加到堆栈顶部?

基本上,当存在某个层次结构是否违反LSP的问题时,这取决于您提出的合同。那么,有什么合同add_after呢?如果听起来很疯狂(例如“在第n个位置或在顶部添加节点”),那很好,则满足后置条件,不违反LSP。否则,这是违反LSP的行为。

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.