【设计模式】里氏代换原则(Liskov Substitution Principle)

任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

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

假如我们把一个普通长方形赋值给GraphicSystemRectangle属性,调用Resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;
假如我们把一个正方形赋值给GraphicSystemRectangle属性,调用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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值