首页 > 解决方案 > 打破里氏替换原则

问题描述

我有以下代码

public class A 
{
      public double foo(double y) 
      {
             return real_value;
      }
}

foo其中方法的输入-1 < y < 1和函数的结果是大于零的实数。

然后我继承了class B继承class A并覆盖了方法foo

public class B extends A 
{
      public override double foo(double y)
      {
         return real_value;
      }
}

foo其中方法的输入0 < y < 1和函数的结果是任意实数。

这里是否违反了里氏替换原则?

提前致谢。

标签: software-designsolid-principlesliskov-substitution-principle

解决方案


假设您想在程序中使用 B 作为 A 的子类型:
是的,您的代码显然违反了 LSK。

为什么?
参数应该是逆变的。

这意味着什么?
Liskov 原则保证,如果您的子类型 B 被基类型 A 替换,您的程序的行为不会改变。

或者更准确地说(Barbara Liskov,1987 年):

“如果对于类型 B 的每个对象 o1 都有一个类型 A 的对象 o2 ,
那么对于以 A 定义的所有程序 P ,
当 o1 替换 o2 时 P 的行为不变,则 B 是 A 的子类型” .

例如:

   class Duck               { void fly(int   height) {} }
   class RedheadDuck : Duck { void fly(long  height) {} }
   class RubberDuck : Duck  { void fly(short height) {} }

   class LSPDemo
   {
      public void Main()
      {
         Duck p;

         p = new Duck();
         p.fly(int.MaxValue); // Expected behaviour

         p = new RedheadDuck();
         p.fly(int.MaxValue); // OK   

         p = new RubberDuck();
         p.fly(int.MaxValue); // Fail 
      }
   }

=> the program behaves unchanged, if the argument is contravariant.
=> e.g. base type <= sub type
=> RubberDuck violates this principle, as it does not allow all values of the base type Duck

在您的代码中,基类 A foo 的类型期望参数值 -1 < y < 1
您的子类 B foo 期望参数值 0 < y < 1
如果您的程序将用基类替换子类,则您的程序对于 foo 关于值 <= 0 的行为不会像预期的那样。

编辑:虽然您在两个 foo 方法上都使用 double 作为参数的类型,但我假设您通过检查值及其范围来保护您的方法。这将导致描述的失败,类似于示例。

PS:是的,这取决于您为 foo 定义的合同。假设你想使用 B 作为 A 的子类型,那么它违反了 LSK。否则它只是一个方法覆盖。


推荐阅读