设计模式六大原则之单一里氏替换原则讲解
小白浏览:4962022-09-06 21:25:44本文累计收益:0我也要赚钱

最近学习设计模式六大原则,简单记录一下学习内容,方便以后复习查阅,如有理解的不对请评论指正。设计模式可以简单理解为面向对象语言开发过程中,遇到各种场景和问题,解决方案和思路。设计模式六大原则为:单一职责原则、里氏替换原则、迪米特法则、依赖倒置原则、接口隔离原则、开闭原则。  

一、什么是里式替换原则
 1、里式替换原则定义

里式替换原则是用来帮助我们在继承关系中进行父子类的设计。

里氏替换原则(Liskov Substitution principle)是对子类型的特别定义的. 为什么叫里式替换原则呢?因为这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

里式替换原则有两层定义:

定义1

If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program。
如果S是T的子类,则T的对象可以替换为S的对象,而不会破坏程序。

定义2:

Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。
所有引用其父类对象方法的地方,都可以透明的替换为其子类对象

这两种定义方式其实都是一个意思,即:应用程序中任何父类对象出现的地方,我们都可以用其子类的对象来替换,并且可以保证原有程序的逻辑行为和正确性。

2、里氏替换原则有至少有两种含义

里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。

如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。

不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。

二. 使用里式替换原则的目的

采用里氏替换原则就是为了减少继承带来的缺点,增强程序的健壮性,版本升级时也可以保持良好的兼容性。即使增加子类,原有的子类也可以继续运行。

三、实例代码
1、首先定义类
public class People
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public void Traditional() {
            Console.WriteLine("仁义礼智信");
        }
    }
    public class Chinese : People { 
        public string Kuaizi { get; set; }
        public void SayHi() {
            Console.WriteLine("早上好,吃了吗?");
        }
    }
    public class Hubei : Chinese { 
        public string Majiang { get; set; }
        public new void SayHi() {
            Console.WriteLine("早上好,过早了吗?");
        }
    }

三个类依次继承,湖北人继承自中国人,中国人继承自人。

2、调用代码
Chinese people1 = new Chinese();
people1.Traditional();

Chinese people2 = new Hubei();
people2.Traditional();
people2使用的new Hubei()替代了people1等号右侧代码 new Chinese(),实现子类替换父类。
四、扩展知识

下面说说,子类替换父类后,如果子类与父类的方法一样,是调用父类的方法还是调用子类的方法,调用原则是什么?首先看下面定义的类,一个父类一个子类。

    public abstract class ParentClass
    {
        public void CommonMethod()
        {
            Console.WriteLine("ParentClass CommonMethod");
        }
        public virtual void VirtualMethod() {
            Console.WriteLine("ParentClass VirtualMethod");
        }
        public virtual void VirtualMethod(string name) {
            Console.WriteLine("ParentClass VirtualMethod");
        }
        public abstract void AbstractMethod();
    }
    public class ChildClass : ParentClass {
        public new void CommonMethod()
        {
            Console.WriteLine("ChildClass CommonMethod");
        }
        public void CommonMethod(string name)
        {
            Console.WriteLine("ChildClass CommonMethod");
        }
        public override void VirtualMethod()
        {
            Console.WriteLine("ChildClass VirtualMethod");
        }
        public override void VirtualMethod(string name)
        {
            Console.WriteLine("ChildClass VirtualMethod");
        }
        public override void AbstractMethod()
        {
            Console.WriteLine("ChildClass AbstractMethod");
        }
    }

调用代码如下:

ParentClass instance = new ChildClass();
instance.CommonMethod();  //普通方法由左边决定,编译时决定
instance.VirtualMethod(); //虚方法由右边决定,运行时决定
instance.AbstractMethod();//抽象方法由右边决定,运行时决定

执行结果如下:

通过执行结果可以看出,调用原则如下:

1、普通方法由左边决定,编译时决定
2、虚方法由右边决定,运行时决定
3、抽象方法由右边决定
评论列表
发表评论
+ 关注