Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我怎样才能避免违反利斯科夫替换原则(LSP)?_Java_Oop_Liskov Substitution Principle - Fatal编程技术网

Java 我怎样才能避免违反利斯科夫替换原则(LSP)?

Java 我怎样才能避免违反利斯科夫替换原则(LSP)?,java,oop,liskov-substitution-principle,Java,Oop,Liskov Substitution Principle,我所处的情况与CodeComplete中提到的非常相似。只是我的问题是基于车辆和三轮车碰巧是在法律上属于汽车的类别。到目前为止,汽车有四个轮子。无论如何,我的领域是不必要的复杂,所以很容易坚持下面的例子 对重写例程且内部不做任何操作的类持怀疑态度 派生例程这通常表示设计中的错误 基类。例如,假设您有一个类Cat和一个 例程Scratch()并假设您最终发现 猫被除草了,不能抓东西。您可能想创建一个 从名为ScratchlessCat的Cat派生的类,并重写Scratch() 例行公事什么也不做。

我所处的情况与CodeComplete中提到的非常相似。只是我的问题是基于车辆和三轮车碰巧是在法律上属于汽车的类别。到目前为止,汽车有四个轮子。无论如何,我的领域是不必要的复杂,所以很容易坚持下面的例子

对重写例程且内部不做任何操作的类持怀疑态度 派生例程这通常表示设计中的错误 基类。例如,假设您有一个类Cat和一个 例程Scratch()并假设您最终发现 猫被除草了,不能抓东西。您可能想创建一个 从名为ScratchlessCat的Cat派生的类,并重写Scratch() 例行公事什么也不做。这种方法存在几个问题:

它违反了Cat中提供的抽象(接口契约) 通过更改其接口的语义来初始化

当您将此方法扩展到其他应用程序时,它很快就会失控 派生类。当你发现一只没有尾巴的猫时会发生什么?或者 不抓老鼠的猫?还是一只不喝牛奶的猫? 最终,您将得到如下派生类 无刮痕、无光泽、无云母、无牛奶

随着时间的推移,这种方法会产生让人困惑的代码 维护,因为祖先类的接口和行为 对他们后代的行为暗示很少或根本没有暗示

解决这个问题的地方不在基类中,而是在 原始猫类。创建一个Claws类并将其包含在 猫课。根本问题是假设所有的猫都会抓挠, 所以,从源头上解决这个问题,而不是从根本上包扎它 目的地

根据上面他那本伟大的书中的文字。下面是坏消息

父类不必是抽象的

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和其他网站上有多少糟糕的例子和多少误解。