Java 我怎样才能避免违反利斯科夫替换原则(LSP)?
我所处的情况与CodeComplete中提到的非常相似。只是我的问题是基于车辆和三轮车碰巧是在法律上属于汽车的类别。到目前为止,汽车有四个轮子。无论如何,我的领域是不必要的复杂,所以很容易坚持下面的例子 对重写例程且内部不做任何操作的类持怀疑态度 派生例程这通常表示设计中的错误 基类。例如,假设您有一个类Cat和一个 例程Scratch()并假设您最终发现 猫被除草了,不能抓东西。您可能想创建一个 从名为ScratchlessCat的Cat派生的类,并重写Scratch() 例行公事什么也不做。这种方法存在几个问题: 它违反了Cat中提供的抽象(接口契约) 通过更改其接口的语义来初始化 当您将此方法扩展到其他应用程序时,它很快就会失控 派生类。当你发现一只没有尾巴的猫时会发生什么?或者 不抓老鼠的猫?还是一只不喝牛奶的猫? 最终,您将得到如下派生类 无刮痕、无光泽、无云母、无牛奶 随着时间的推移,这种方法会产生让人困惑的代码 维护,因为祖先类的接口和行为 对他们后代的行为暗示很少或根本没有暗示 解决这个问题的地方不在基类中,而是在 原始猫类。创建一个Claws类并将其包含在 猫课。根本问题是假设所有的猫都会抓挠, 所以,从源头上解决这个问题,而不是从根本上包扎它 目的地 根据上面他那本伟大的书中的文字。下面是坏消息 父类不必是抽象的Java 我怎样才能避免违反利斯科夫替换原则(LSP)?,java,oop,liskov-substitution-principle,Java,Oop,Liskov Substitution Principle,我所处的情况与CodeComplete中提到的非常相似。只是我的问题是基于车辆和三轮车碰巧是在法律上属于汽车的类别。到目前为止,汽车有四个轮子。无论如何,我的领域是不必要的复杂,所以很容易坚持下面的例子 对重写例程且内部不做任何操作的类持怀疑态度 派生例程这通常表示设计中的错误 基类。例如,假设您有一个类Cat和一个 例程Scratch()并假设您最终发现 猫被除草了,不能抓东西。您可能想创建一个 从名为ScratchlessCat的Cat派生的类,并重写Scratch() 例行公事什么也不做。
public abstract class Cat {
public void scratch() {
System.out.println("I can scratch");
}
}
派生类
public class ScratchlessCat extends Cat {
@Override
public void scratch() {
// do nothing
}
}
现在他建议创建另一个类
Claws
,但我不明白如何使用这个类来避免使用ScratchlessCat#Scratch
您仍然会有一个Scratch()
方法,但它不会被派生类覆盖:
public class Cat {
Claw claw_;
public Cat(Claw claw) {claw = claw_;}
public final void scratch() {
if (claw_ != null) {
claw_.scratch(this);
}
}
}
这允许您将抓取逻辑委托给包含的爪
对象(如果存在)(如果没有爪,则不进行抓取)。从cat派生的类在如何抓取方面没有发言权,因此不需要基于能力创建阴影层次结构
此外,由于派生类无法更改方法实现,因此它们不会破坏基类接口中scratch()
方法的预期语义
如果你把它推向极端,你可能会发现你有很多类,而没有很多派生——大多数逻辑都委托给组合对象,未委托给派生类。并非所有的猫都有爪并且能够抓挠,这是一个很大的线索,
Cat
不应该在其API中定义公共的scratch
方法。第一步是首先考虑为什么定义了代码>划痕< /代码>。也许猫在受到攻击时会抓挠;如果没有,他们就会发出嘶嘶声或逃跑
public class Cat extends Animal {
private Claws claws;
public void onAttacked(Animal attacker) {
if (claws != null) {
claws.scratch(attacker);
}
else {
// Assumes all cats can hiss.
// This may also be untrue and you need Voicebox.
// Rinse and repeat.
hiss();
}
}
}
现在,您可以将任何
Cat
子类替换为另一个子类,它的行为将根据是否有爪而正确。您可以定义一个防御机制
类来组织各种防御,例如刮伤
、嘶嘶声
、咬伤
,等等。Cat示例是LSP的一个很好的说明。在学习LSP的过程中,我惊讶于SO和其他网站上有多少糟糕的例子和多少误解。