• 【面向对象设计原则】之里氏替换原则(LSP)


      里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个设计原则。

    里氏替换原则(Liskov Substitution Principle, LSP):所有引用父类的地方必须能使用其子类的对象。

    从这个概念可以看出这个原则是面向对象多态的一种具体实践。通俗来讲 “老爸能干的事情,儿子都能干”, 因为儿子继承了老爸的基因。 反过来讲就不对了,时代在变化,新一代虽然继承了老一代的优良传统,但是在时代的影响下,新一代有了一些新的特性,老一代可能就不具备了,比如现在的年轻人会打游戏,但是他爸不一定会。老爸会骑自行车,换成儿子也能骑。

    同样的里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用父类对象。

    我们定义一个父类叫Animal, 其包含一个方法叫Say如下:

        public class Animal
        {
            private readonly string _sayContent;
    
            public Animal(string sayContent)
            {
                _sayContent = sayContent;
            }
            public virtual void Say()
            {
                Console.WriteLine($"Animal Say:{_sayContent}");
            }
        }

    再定义一个子类Pig 集成自Animal,并覆盖父类中的Say 方法如下:

        public class Pig:Animal
        {
            private readonly string _sayContent;
    
            public Pig(string sayContent) : base(sayContent)
            {
                _sayContent = sayContent;
            }
    
            public override void Say()
            {
                Console.WriteLine($"Pig Say:{_sayContent}");
            }
        }

    现在我们在调用方创建一个Animal的对象并调用Say方法:

                Animal animal = new Animal("This is a parent class.");
                animal.Say();

    输出结果:

    Animal Say:This is a parent class.

    下来我们创建一个Pig对象赋给animal 对象并调用Say方法:

            static void Main(string[] args)
            {
                Animal animal = new Animal("This is a parent class.");
                animal.Say();
    
                animal = new Pig("This is a sub class.");
                animal.Say();
    
                Console.ReadKey();
            }

    输出:

    image

    可以看出将子类的对象赋给父类的对象,并且得到了我们期望的结果。

    里氏替换原则是实现开闭原则的重要方式之一(其实其它原则都是实现开闭原则OCP重要方式之一,上一篇【面向对象设计原则】之开闭原则(OCP) 有提及),由于使用父类对象的地方都可以使用子类对象,因此在程序中尽量使用父类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。通常我们会使用接口或者抽象方法定义基类,然后子类中实现父类的方法,并在运行时通过各种手段进行类型选择调用(比如反射)。

    在使用里氏替换原则时需要注意如下几个问题:

          (1)子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏替换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。

          (2)  我们在运用里氏替换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏替换原则是开闭原则的具体实现手段之一。这也就是我们应该更多的依赖抽象,尽量少的依赖实现细节, 其实就是我们下一篇要讲的依赖倒置原则(DIP)。

  • 相关阅读:
    第三方模块paramiko的使用
    第三方模块paramiko的使用
    第三方模块paramiko的使用
    共识算法的比较:Casper vs Tendermint
    区块链危机!危险的共识算法
    优化的bft共识算法的设计和使用方式
    Obelisk 共识算法设计动机
    信任的 7000 年历史
    【CS231n】斯坦福大学李飞飞视觉识别课程笔记(五):图像分类笔记(下)
    【CS231n】斯坦福大学李飞飞视觉识别课程笔记(四):图像分类笔记(上)
  • 原文地址:https://www.cnblogs.com/vaiyanzi/p/6899402.html
Copyright © 2020-2023  润新知