sublcass不应该改变父母的行为吗?
这是对LSP的常见误解。子类可以更改父代的行为,只要它对父代类型保持正确即可。
在Wikipedia上有一个很好的长期解释,建议将破坏LSP的事情:
...该子类型必须满足许多行为条件。这些术语在类似于按合同设计方法的术语中进行了详细说明,从而对合同如何与继承交互产生了一些限制:
- 前提条件不能在子类型中得到加强。
- 子条件不能弱化后置条件。
- 超类型的不变量必须保留在子类型中。
- 历史记录约束(“历史记录规则”)。对象只能通过其方法(封装)被视为可修改的。由于子类型可能会引入父类型中不存在的方法,因此,这些方法的引入可能会导致子类型中状态不允许在父类型中发生变化。历史记录约束禁止这样做。这是Liskov和Wing引入的新颖元素。可以通过将MutablePoint定义为ImmutablePoint的子类型来举例说明违反此约束的情况。这违反了历史记录约束,因为在不可变点的历史记录中,状态在创建后始终是相同的,因此通常不能包含MutablePoint的历史记录。但是,可以安全地修改添加到子类型的字段,因为它们无法通过超类型方法观察到。
就个人而言,我更容易记住这一点:如果我正在使用类型A的方法查看参数,有人传递子类型B会给我带来什么惊喜吗?如果他们愿意的话,就违反了LSP。
抛出异常是一个惊喜吗?并不是的。无论我是在OrderState上还是在Granted或Shipped上调用Ship方法,这种情况随时都可能发生。因此,我必须考虑到这一点,这并不是违反LSP。
也就是说,我确实认为有更好的方法来处理这种情况。如果我使用C#编写此代码,则将使用接口并在调用方法之前检查接口的实现。例如,如果当前的OrderState没有实现IShippable,则不要在其上调用Ship方法。
但是,在这种情况下,我也不会使用State模式。状态模式比这样的域对象的状态更适合应用程序的状态。
因此,简而言之,这是状态模式的一个拙劣设计示例,不是处理订单状态的特别好方法。但是可以说它没有违反LSP。与国家的模式,和其本身的,肯定没有。