任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。
1 案例分析(正方形不是长方形)
在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。我们开发的一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承自长方形。
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
var graphicSystem = new GraphicSystem();
var rectangle = new Rectangle();
rectangle.SetLength(20);
rectangle.SetWidth(10);
graphicSystem.Rectangle = rectangle;
graphicSystem.Resize();
graphicSystem.PrintLengthAndWidth();
Console.WriteLine("============");
var square = new Square();
square.SetLength(10);
graphicSystem.Rectangle = square;
graphicSystem.Resize();
graphicSystem.PrintLengthAndWidth();
}
}
/// <summary>
/// 图形系统
/// </summary>
public class GraphicSystem
{
/// <summary>
/// 基类对象属性
/// </summary>
public Rectangle Rectangle { get; set; }
/// <summary>
/// 当宽度小于等于长度时,直到宽度大于长度
/// </summary>
public void Resize()
{
var index = 0;
while (this.Rectangle.GetWidth() <= this.Rectangle.GetLength())
{
Console.WriteLine($"正在执行第 {index++} 次循环");
this.Rectangle.SetWidth(this.Rectangle.GetWidth() + 1);
}
}
/// <summary>
/// 打印当前的长度和宽度
/// </summary>
public void PrintLengthAndWidth()
{
Console.WriteLine(this.Rectangle.GetLength());
Console.WriteLine(this.Rectangle.GetWidth());
}
}
/// <summary>
/// 矩形类
/// </summary>
public class Rectangle
{
/// <summary>
/// 矩形长度
/// </summary>
private double length;
/// <summary>
/// 矩形宽度
/// </summary>
private double width;
/// <summary>
/// 获取长度
/// </summary>
/// <returns>长度</returns>
public double GetLength() => this.length;
/// <summary>
/// 设置长度
/// </summary>
/// <param name="length">长度</param>
public virtual void SetLength(double length) => this.length = length;
/// <summary>
/// 获取宽度
/// </summary>
/// <returns>宽度</returns>
public double GetWidth() => this.width;
/// <summary>
/// 设置宽度
/// </summary>
/// <param name="width">宽度</param>
public virtual void SetWidth(double width)=>this.width = width;
}
/// <summary>
/// 正方形类
/// </summary>
public class Square : Rectangle
{
/// <summary>
/// <inheritdoc cref="Rectangle"/>
/// </summary>
/// <param name="width">宽度</param>
public override void SetWidth(double width)
{
base.SetLength(width);
base.SetWidth(width);
}
/// <summary>
/// <inheritdoc cref="Rectangle"/>
/// </summary>
/// <param name="length">长度</param>
public override void SetLength(double length)
{
base.SetLength(length);
base.SetWidth(length);
}
}
}
假如我们把一个普通长方形
赋值给GraphicSystem
的Rectangle
属性,调用Resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;
假如我们把一个正方形
赋值给GraphicSystem
的Rectangle
属性,调用Resize方法,就会看到正方形的宽度和长度都在不断增长
,代码会一直运行下去,直至系统产生溢出错误。所以,普通的长方形是适合这段代码的,正方形不适合。因此,Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。
2 案例改进
我们需要重新设计他们之间的关系。抽象出来一个四边形接口(Quadrilateral),让Rectangle类和Square类实现Quadrilateral接口。
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
var graphicSystem = new GraphicSystem();
var rectangle = new Rectangle();
rectangle.SetLength(20);
rectangle.SetWidth(10);
graphicSystem.Quadrilateral = rectangle;
graphicSystem.Resize();
graphicSystem.PrintLengthAndWidth();
Console.WriteLine("============");
var square = new Square();
square.SetSide(10);
graphicSystem.Quadrilateral = square;
graphicSystem.Resize();
graphicSystem.PrintLengthAndWidth();
}
}
/// <summary>
/// 图形系统
/// </summary>
public class GraphicSystem
{
/// <summary>
/// 基类对象属性
/// </summary>
public Quadrilateral Quadrilateral { get; set; }
/// <summary>
/// 当宽度小于等于长度时,直到宽度大于长度
/// </summary>
public void Resize()
{
if (!(this.Quadrilateral is Rectangle rectangle))
return;
var index = 0;
while (this.Quadrilateral.GetWidth() <= this.Quadrilateral.GetLength())
{
Console.WriteLine($"正在执行第 {index++} 次循环");
rectangle.SetWidth(this.Quadrilateral.GetWidth() + 1);
}
}
/// <summary>
/// 打印当前的长度和宽度
/// </summary>
public void PrintLengthAndWidth()
{
Console.WriteLine(this.Quadrilateral.GetLength());
Console.WriteLine(this.Quadrilateral.GetWidth());
}
}
/// <summary>
/// 四边形基类
/// </summary>
public interface Quadrilateral
{
/// <summary>
/// 获取长度
/// </summary>
/// <returns>长度</returns>
double GetLength();
/// <summary>
/// 获取宽度
/// </summary>
/// <returns>宽度</returns>
double GetWidth();
}
/// <summary>
/// 矩形类
/// </summary>
public class Rectangle : Quadrilateral
{
/// <summary>
/// 矩形长度
/// </summary>
private double length;
/// <summary>
/// 矩形宽度
/// </summary>
private double width;
/// <summary>
/// <inheritdoc cref="Quadrilateral"/>
/// </summary>
/// <returns>长度</returns>
public double GetLength() => this.length;
/// <summary>
/// 设置长度
/// </summary>
/// <param name="length">长度</param>
public void SetLength(double length) => this.length = length;
/// <summary>
/// <inheritdoc cref="Quadrilateral"/>
/// </summary>
/// <returns>宽度</returns>
public double GetWidth() => this.width;
/// <summary>
/// 设置宽度
/// </summary>
/// <param name="width">宽度</param>
public void SetWidth(double width)=>this.width = width;
}
/// <summary>
/// 正方形类
/// </summary>
public class Square : Quadrilateral
{
/// <summary>
/// 正方向边长
/// </summary>
private double side;
/// <summary>
/// 设置正方向边长
/// </summary>
/// <param name="side">边长</param>
public void SetSide(double side)=>this.side = side;
/// <summary>
/// <inheritdoc cref="Quadrilateral"/>
/// </summary>
/// <returns>长度</returns>
public double GetLength() => this.side;
/// <summary>
/// <inheritdoc cref="Quadrilateral"/>
/// </summary>
/// <returns>宽度</returns>
public double GetWidth() => this.side;
}
}