违反Liskov替换原则

3

我有以下代码

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

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

然后我有一个继承类B,它继承了A类并重写了foo方法。

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

这里的foo方法输入参数范围为0 < y < 1,函数结果可以是任何实数。

这里违反了里氏替换原则吗?

提前感谢。


2
取决于 foo 的契约是什么。您已经描述了特定实现的行为,但没有描述一般契约。 - user2357112
2个回答

6

假设您的程序中想要使用B作为A的子类型:

是的,您的代码明显违反了LSK原则。

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

这是什么意思?
Liskov原则保证,如果将子类型B替换为基本类型A,则程序的行为不会改变。

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

“如果对于类型B的每个对象o1,都有类型A的对象o2,
使得对于所有定义在A的程序P,在用o2替换o1时,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
如果你的程序将子类替换成基类,那么在关于值<=0的情况下,你的程序不会按预期运行。
编辑:尽管你在两个foo方法中都使用了double类型作为参数,我假设你通过检查值和它们的范围来保护你的方法。这将导致所描述的失败,类比于示例。
附言:是的,它取决于你为foo定义的合同。假设你想将B用作A的子类型,那么它违反了LSK。否则,它只是一种方法重写。

0

实际上,您重写的函数违反了基本函数的契约,因此Liskov原则在这里并不真正有用。但是,如果您向基本函数添加一个参数,例如“处理模式”,则L原则将完美地发挥作用(重写的函数将为所有旧的处理情况调用基本函数)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接