设计模式(六)之里氏替换原则

简介: 里氏替换原则,为继承定义规范。里氏替换原则有如下特点:代码共享,减少创建类的工作量、提高代码的重用性、提高代码的可扩展性、提高产品代码的开放性、继承侵入性 只要继承,必须拥有父类的内容、降低代码的灵活性,子类必须拥有父类的属性和方法、增强耦合性。

QQ图片20220424153550.jpg

里氏替换原则,为继承定义规范。


里氏替换原则有如下特点:


代码共享,减少创建类的工作量


提高代码的重用性


提高代码的可扩展性


提高产品代码的开放性


继承侵入性 只要继承,必须拥有父类的内容


降低代码的灵活性,子类必须拥有父类的属性和方法


增强耦合性。


里氏替换原则:


有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。


举个例子:


我这里有一个长方形类,一个正方形类,正方形是一个特殊的长方形。那么正方形类继承自长方形类:


代码如下所示:


Program.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 高层模块:调用长方形及正方形类
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Rectangle rectangle = new Rectangle();
            rectangle.SetHeight(10);
            rectangle.SetWidth(20);
            Console.WriteLine(rectangle.GetHeight());
            Console.WriteLine(rectangle.GetWidth());
            Console.WriteLine("------------  我是分割线  -------------");
            Square square = new Square();
            square.SetHeight(10);
            square.SetWidth(20);
            Console.WriteLine(square.GetHeight());
            Console.WriteLine(square.GetWidth());
            Console.ReadKey();
        }
    }
}


长方形类:Rectangle.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 长方形类
    /// </summary>
    public class Rectangle
    {
        public double height;
        public double width;
        public void SetHeight(double height)
        {
            this.height = height;
        }
        public double GetHeight()
        {
            return height;
        }
        public void SetWidth(double width)
        {
            this.width = width;
        }
        public double GetWidth()
        {
            return width;
        }
    }
}


正方形类:Square.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 正方形类
    /// </summary>
    public class Square: Rectangle
    {
        public void SetHeight(double height)
        {
            this.height = height;
            this.width = height;
        }
        public double GetHeight()
        {
            return height;
        }
        public void SetWidth(double width)
        {
            this.height = width;
            this.width = width;
        }
        public double GetWidth()
        {
            return width;
        }
        /// <summary>
        /// 求面积
        /// </summary>
        public double GetArea()
        {
            return width * height;
        }
    }
}


输出结果如下图所示:


QQ图片20220424153553.png


输出的结果明显是不对的,我调用父类与子类的统一方法输出的结果应该是一致的。所以上面的栗子不符合里氏替换原则。


里氏替换原则: 只要有父类出现的地方,都可以用子类来替代,而且不会出现任何错误和异常。但是反过来则不行,有子类出现的地方,不能用其父类替代。 包含以下四种约束: 1:子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。 有时候父类有多个子类,但在这些子类中有一个特例。要想满足里氏替换原则,又想满足这个子类的功能时,有的伙伴可能会修改父类的方法。但是,修改了父类的方法又会对其他的子类造成影响,产生更多的错误。这是怎么办呢?我们可以为这个特例创建一个新的父类,这个新的父类拥有原父类的部分功能,又有不同的功能。这样既满足了里氏替换原则,又满足了这个特例的需求。 2:子类中可以增加自己特有的方法。 3:当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。(子类的参数范围要比父类的参数范围大) 4:当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。


我上面的那段程序很显然是不符合里氏替换原则的,那么上边我们那段程序该怎么修改呢,我们创建一个接口,接口中包含获取宽度及高度的两个方法,长方形类和正方形类分别实现这个接口,那么其二者现在就为同级,父类可以的地方,他们二者都可以。下边是我修改之后的代码:


Program.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 高层模块:调用长方形及正方形类
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Rectangle rectangle = new Rectangle();
            rectangle.SetHeight(10);
            rectangle.SetWidth(20);
            Console.WriteLine(rectangle.GetHeight());
            Console.WriteLine(rectangle.GetWidth());
            Console.WriteLine("------------  我是分割线  -------------");
            Square square = new Square();
            square.SetHeight(10);
            square.SetWidth(20);
            Console.WriteLine(square.GetHeight());
            Console.WriteLine(square.GetWidth());
            Console.ReadKey();
        }
    }
}


Rectangle.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 长方形类
    /// </summary>
    public class Rectangle: Interface1
    {
        public double height;
        public double width;
        public void SetHeight(double height)
        {
            this.height = height;
        }
        public double GetHeight()
        {
            return height;
        }
        public void SetWidth(double width)
        {
            this.width = width;
        }
        public double GetWidth()
        {
            return width;
        }
    }
}


Square.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    /// <summary>
    /// 正方形类
    /// </summary>
    public class Square: Interface1
    {
        public double height;
        public double width;
        public void SetHeight(double height)
        {
            this.height = height;
            this.width = height;
        }
        public double GetHeight()
        {
            return height;
        }
        public void SetWidth(double width)
        {
            this.height = width;
            this.width = width;
        }
        public double GetWidth()
        {
            return width;
        }
        /// <summary>
        /// 求面积
        /// </summary>
        public double GetArea()
        {
            return width * height;
        }
    }
}


父类接口:Interface1.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Displace
{
    interface Interface1
    {
        double GetHeight();
        double GetWidth();
    }
}


这个程序基本上就实现了上边所说的四个约束,例子有点烂,但是基本上融汇了里氏替换原则的思想。


说到底,里氏替换原则就是对继承的约束~



目录
相关文章
|
5月前
|
设计模式
设计模式 六大原则之里氏替换原则
设计模式 六大原则之里氏替换原则
|
6月前
|
设计模式 Java 开发者
Java设计模式七大原则之里氏替换原则
Java设计模式七大原则之里氏替换原则
53 0
|
设计模式
设计模式(4) --里氏替换原则
设计模式(4) --里氏替换原则
设计模式(4) --里氏替换原则
|
设计模式
设计模式七大原则——里氏替换原则
设计模式七大原则——里氏替换原则
设计模式七大原则——里氏替换原则
|
设计模式 Java
【Java设计模式】如何正确的使用继承?里氏替换原则的使用
【Java设计模式】如何正确的使用继承?里氏替换原则的使用
【Java设计模式】如何正确的使用继承?里氏替换原则的使用
|
设计模式
【设计模式】软件设计七大原则 ( 里氏替换原则 | 定义 | 定义扩展 | 引申 | 意义 | 优点 )
【设计模式】软件设计七大原则 ( 里氏替换原则 | 定义 | 定义扩展 | 引申 | 意义 | 优点 )
206 0
|
设计模式
设计模式—— 二:里氏替换原则
设计模式—— 二:里氏替换原则
123 0
设计模式—— 二:里氏替换原则
|
设计模式 安全 Java
可能是最好的设计模式入门教程——里氏替换原则
可能是最好的设计模式入门教程——里氏替换原则
387 0
可能是最好的设计模式入门教程——里氏替换原则
|
设计模式
【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )(一)
【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )(一)
112 0
|
设计模式
【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )(三)
【设计模式】软件设计七大原则 ( 里氏替换原则 | 代码示例 | 类示例 | 方法入参示例 | 方法返回值示例 )(三)
102 0

热门文章

最新文章

  • 1
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    44
  • 2
    C++一分钟之-C++中的设计模式:单例模式
    51
  • 3
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    36
  • 4
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    59
  • 5
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    54
  • 6
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    39
  • 7
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 8
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    102
  • 9
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    74
  • 10
    Go语言设计模式:使用Option模式简化类的初始化
    71