c# - 为什么这个类层次结构违反 LSP?
问题描述
下面是来自在线教程的代码(https://code-maze.com/liskov-substitution-principle/):
// version 1
public class SumCalculator
{
protected readonly int[] _numbers;
public SumCalculator(int[] numbers)
{
_numbers = numbers;
}
public virtual int Calculate() => _numbers.Sum();
}
public class EvenNumbersSumCalculator: SumCalculator
{
public EvenNumbersSumCalculator(int[] numbers)
:base(numbers)
{
}
public override int Calculate() => _numbers.Where(x => x % 2 == 0).Sum();
}
那么我们可以这样做:
class Program
{
static void Main(string[] args)
{
var numbers = new int[] { 5, 7, 9, 8, 1, 6, 4 };
SumCalculator sum = new SumCalculator(numbers);
Console.WriteLine($"The sum of all the numbers: {sum.Calculate()}");
Console.WriteLine();
SumCalculator evenSum = new EvenNumbersSumCalculator(numbers);
Console.WriteLine($"The sum of all the even numbers: {evenSum.Calculate()}");
}
}
所以我们可以把子实例new EvenNumbersSumCalculator(numbers)
(SumCalculator evenSum
但是教程说版本 1 不符合 Liskov 原则,我们需要这样做:
// version 2
public abstract class Calculator
{
protected readonly int[] _numbers;
public Calculator(int[] numbers)
{
_numbers = numbers;
}
public abstract int Calculate();
}
public class SumCalculator : Calculator
{
public SumCalculator(int[] numbers)
:base(numbers)
{
}
public override int Calculate() => _numbers.Sum();
}
public class EvenNumbersSumCalculator: Calculator
{
public EvenNumbersSumCalculator(int[] numbers)
:base(numbers)
{
}
public override int Calculate() => _numbers.Where(x => x % 2 == 0).Sum();
}
class Program
{
static void Main(string[] args)
{
var numbers = new int[] { 5, 7, 9, 8, 1, 6, 4 };
Calculator sum = new SumCalculator(numbers);
Console.WriteLine($"The sum of all the numbers: {sum.Calculate()}");
Console.WriteLine();
Calculator evenSum = new EvenNumbersSumCalculator(numbers);
Console.WriteLine($"The sum of all the even numbers: {evenSum.Calculate()}");
}
}
我不明白为什么版本 1 不符合 Liskov 原则?
解决方案
您version 1
与 LSP 不兼容,与您的示例程序无关。
您可以“将子实例存储到父变量中”这一事实是C# 支持的子类型化的语法概念。LSP 提供了子类型的行为概念,坚持超类型的含义(语义)保留在任何子类中。
在version 1
中,超类Calculate
方法计算所有数字,但在子类中只计算偶数。这使得子类的行为与超类不一致。
在 LSP-compliantversion 2
中,通过添加一个没有任何行为的单独类并在两个独立的子类型中对其进行两次扩展来避免这种情况。这是符合 LSP 的。
如果您正在寻找一个想要保持超类行为的示例,请考虑一个除了计算之外还做一些额外事情的类,例如一个LoggingCalculator
方法Calculate
首先调用超类方法(保持相同的行为)然后扩展它通过在某处记录结果。
推荐阅读
- nginx - 如何创建 TCP 侦听器?
- sql - SQL 查询未得到结果
- java - 如何以编程方式调整设备的蓝牙灵敏度
- amazon-web-services - Amazon EC2 UTC 时区显示错误
- mysql - 如何支持基于 MySQL 中插入的数据创建新列
- android - FormsSeekBar.setPressed。在 Android 的 xamarin.forms 中引发了“Java.Interop.JavaLocationException”类型的异常
- android - 如何突出显示颤动中的选择文本?
- jquery - 按值 Jquery 设置 url 树枝
- android - 在简单的数组添加上,RenderScript 的运行速度比 Kotlin 慢
- javascript - 用数组值过滤数组对象