How can I avoid breaking Liskov Substitution Principle (LSP)?

旧城冷巷雨未停 提交于 2019-11-30 03:57:53
Attila

You would still have a scratch() method, but it will not be overridden by the derived classes:

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

This allows you to delegate the scratching logic to the contained Claw object, if present (and not scratch if there are no claws). Classes derived from cat have no say in the matter on how to scratch, so no need to create shadow hierarchies based on abilities.

Also, because the derived classes cannot change the method implementation, there is no problem of them breaking the intended semantics of the scratch() method in the base class's interface.

If you take this to the extremes, you might find that you have a lot of classes and not many derivations -- most logic is delegated to the composition objects, not entrusted to the derived classes.

That not all cats have claws and are capable of scratching is a big clue that Cat should not define a public scratch method in its API. The first step is to consider why you defined scratch in the first place. Perhaps cats scratch if they can when attacked; if not they hiss or run away.

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();
        }
    }
}

Now you can substitute any Cat subclass for another and it will behave correctly depending on whether or not it has claws. You could define a DefenseMechanism class to organize the various defenses such as Scratch, Hiss, Bite, etc.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!