Inheritance LSP对强化先决条件的限制是否与需要降级表明设计糟糕的建议相冲突

Inheritance LSP对强化先决条件的限制是否与需要降级表明设计糟糕的建议相冲突,inheritance,class-design,downcast,class-hierarchy,liskov-substitution-principle,Inheritance,Class Design,Downcast,Class Hierarchy,Liskov Substitution Principle,我最近开始阅读关于Liskov替换原则()的内容,我正在努力完全理解“子类型中的先决条件无法加强”这一限制的含义。在我看来,这种限制似乎与设计原则相冲突,设计原则建议人们应该尽量减少或完全避免从基类向下转换为派生类的需要 也就是说,我从一个动物类开始,派生出动物狗,鸟,和人。LSP对先决条件的限制显然符合自然,因为狗、鸟或人都不应该比一般动物受到更大的限制。遵循LSP,派生类随后将添加一些特殊特性,例如Bird.fly()或Human.makeTool(),这些特性对于Animal来说并不常见

我最近开始阅读关于Liskov替换原则()的内容,我正在努力完全理解“子类型中的先决条件无法加强”这一限制的含义。在我看来,这种限制似乎与设计原则相冲突,设计原则建议人们应该尽量减少或完全避免从基类向下转换为派生类的需要

也就是说,我从一个
动物
类开始,派生出动物
,和
。LSP对先决条件的限制显然符合自然,因为狗、鸟或人都不应该比一般动物受到更大的限制。遵循LSP,派生类随后将添加一些特殊特性,例如
Bird.fly()
Human.makeTool()
,这些特性对于
Animal
来说并不常见

基类
Animal
对每个可能的动物子类型的每个可能的特征都有虚拟方法,这感觉有点荒谬,但是如果没有,那么我需要向下引用其基础子类型
Animal
来访问这些独特的特征。然而,这一需要被普遍认为是糟糕设计的危险信号。维基百科甚至认为这是一个错误

那么我错过了什么


奖金问题:再次考虑上面描述的<代码>动物< /代码>的等级结构。显然,如果

Animal.setWeight(weight)
只需要一个非负数,而
Human.setWeight(weight)
强化了这一前提条件,并要求非负数小于1000,那么这将是LSP违规。但是
人类
的构造器呢,它可能看起来像
人类(体重、身高、性别)
?如果建造商对重量施加限制,是否会违反LSP?如果是这样,该如何重新设计这种层次结构,以尊重衍生动物物理特性的明确界限?

编程的许多方面都涉及权衡,其中包括坚实的原则。如果有一些类型的操作可以以相同的方式对一个类的几乎所有派生或接口的实现执行,并且实际上不是接口的主要目的的一部分,但是一些特定的派生或实现可能有更好的执行方式,那么“接口分离原则”建议在公共接口(*)中不包括此类操作。在这种情况下,接收对非特定类型的引用的代码检查实际对象是否具有某些“特殊”特性,如果是,则使用这些特性可能会有所帮助。例如,接收一个
IEnumerable
并想知道它包含多少项的代码可以检查它是实现
ICollection
还是非泛型
ICollection
[注意
List
实现后者而不是前者],如果是,则向下转换并使用
Count
方法]。在这种情况下,向下转换没有什么错,因为该方法不要求传入的实例实现这些接口——当它们实现这些接口时,它只会工作得更好

(*)IMHO,
IEnumerable
应该包含一个方法来描述序列的属性,例如计数是否已知,它是否将永远包含相同的项,等等,但它没有

向下投射的另一种用法是,当一个人拥有一组对象,并且知道每个组中的特定对象实例彼此“兼容”时,即使一个组中的对象可能与另一个组中的对象不兼容。例如,
MaleCat.MateWith()
方法可能只接受
FemaleCat
的实例,而
femaleakangaro.MateWith()
with可能只接受
maleangaro()的实例,但是诺亚收集配对动物最实用的方法是每种动物都有一个
MateWith()
方法,该方法接受
animal
并将其降级到合适的类型(可能还有一个
CanMateWith()
属性)。如果构建的
交配对
包含一只
雌性仓鼠
雄性狼
,则在该交配对上调用
繁殖()
方法的尝试将在运行时失败,但如果代码避免构建不兼容的交配对,则此类失败将永远不会发生。请注意,泛型可以大大减少这种向下转换的需要,但不能完全消除它


在确定下行广播是否违反LSP时,5万美元的问题是,一种方法是否会为任何可能被传递的内容维护其合同。如果
MateWith()
方法的契约指定它只保证在
Animal
的特定实例上有效地运行,并且
CanMateWith()
已返回true,那么当给定
Animal
的某些子类型时,它将失败的事实将不会是LSP冲突。一般来说,让方法在编译时拒绝类型不能保证可用的对象是有用的,但在某些情况下,代码可能知道某些对象实例的类型之间的关系,而这些类型不能用语法表示[例如,
配对
将拥有两个可成功繁殖的
动物
实例]。虽然向下投射通常是一种代码气味,但当它以与对象的契约一致的方式使用时,它没有什么问题。

LSP都是关于行为子类型的。粗略地说,
B
a
的一个子类型,如果它总是可以在预期的
a
的地方使用的话。而且,这种用法不应该改变行为

所以,