如何避免破坏Liskov替换原则(LSP)?

时间:2012-05-28 00:59:19

标签: java oop liskov-substitution-principle

我的情况与Code Complete中提到的Steve McConnell's非常相似。只有我的问题是基于车辆和三轮车才恰好依据法律属于汽车类别。到目前为止,汽车有四个轮子。无论如何,我的域名都不必要地复杂,因此很容易坚持使用下面的猫。

  

要怀疑那些覆盖例程而在里面什么都不做的类   派生例程这通常表示设计中的错误   基类。例如,假设你有一个类Cat和一个   例行Scratch()并假设你最终发现了一些   猫是声明的,不能划伤。你可能想要创造一个   派生自Cat的ScratchlessCat并重写Scratch()   例行公事无所事事。这种方法存在几个问题:

     

它违反了Cat中提出的抽象(接口契约)   class通过改变其接口的语义。

     

当您将其扩展到其他方法时,此方法很快就会失控   派生类。当你找到一只没有尾巴的猫时会发生什么?或者a   猫不会抓老鼠?还是一只不喝牛奶的猫?   最终你会得到类似的派生类   ScratchlessTaillessMicelessMilklessCat。

     

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

     

解决此问题的地方不在基类中,而是在基类中   原来的猫类。创建一个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

2 个答案:

答案 0 :(得分:12)

并非所有的猫都有爪子并且能够抓挠是一个很大的线索,Cat不应该在其API中定义公共scratch方法。第一步是考虑您首先定义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子类替换为另一个子类,它将根据是否具有爪子而正确运行。您可以定义DefenseMechanism类来组织各种防御,例如ScratchHissBite等。

答案 1 :(得分:9)

您仍然可以使用scratch()方法,但派生类不会覆盖它:

public class Cat {
  Claw claw_;
  public Cat(Claw claw) {claw = claw_;}
  public final void scratch() {
    if (claw_ != null) {
      claw_.scratch(this);
    }
  }
}

这允许您将抓取逻辑委派给包含的Claw对象(如果存在)(如果没有抓爪则不会刮擦)。从cat派生的类在如何划分的问题上没有发言权,因此不需要根据能力创建阴影层次结构。

此外,因为派生类不能更改方法实现,所以它们没有打破基类接口中scratch()方法的预期语义的问题。

如果你把它带到极端,你可能会发现你有很多类而且没有多少派生 - 大多数逻辑被委托给组合对象,而不是委托给派生类。