精华内容
下载资源
问答
  • 里氏替换原则

    热门讨论 2020-05-13 09:32:59
    什么是里氏替换原则? 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何父类可以出现的地方,子类一定可以出现。 LSP继承复用的基石,只有当派生类可以...

    什么是里氏替换原则?

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何父类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当派生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。(子类可以替换父类)

    因为继承带来的侵入性,增加了耦合性,也降低了代码灵活性,父类修改代码,子类也会受到影响,此时就需要里氏替换原则。

    继承的优缺点

    继承有一些优点:
    1. 提高代码的重用性,子类拥有父类的方法和属性;

    2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;

    缺点:侵入性、不够灵活、高耦合

    1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性,在一定程度上约束了子类,降低了代码的灵活性;

    2. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能会造成非常糟糕的结果,要重构大量的代码。

    里氏替换思维导图

    在这里插入图片描述

    情况举例代码

    情况一:子类对象能够替换其父类
    Student stu=new Student();
    Person p =stu;
    p.Show();
    
    

    情况二:父类对象不能直接转换子类

    Person p=new Person();
    Student stu =(Student)p;
    stu.SayHello();
    

    情况三:new的时候new的是子类,再转换这个子类的时候可以

    Person p=new Teacher();
    Teacher t =(Teacher)p;
    t.SayHi();
    

    情况四:new的是子类,转换的另一个子类,报异常

    Person p =new Teacher();
    Student stu =(Student)p;
    stu.SayHello;
    
    展开全文
  • 你可以不知道继承、多态,但是必须知道里氏替换原则(Liskov SubstitutionPrinciple、LSP)。

    你可以不知道继承、多态,但是必须知道里氏替换原则(Liskov SubstitutionPrinciple、LSP)

    1.要点

    里氏替换原则/LSP是OO编程范式中规则范式的核心。
    它说明(强调)了子类型必须具备替换属性,这在面向对象语言中如同常识。(类层次 Is_a 替代)
    即在一个软件系统中,基类出现的所有地方都必须能够被子类型替代。
    • 子类型必须能够替代其父类型——《敏捷软件开发:原则、模式与实践Robert C. Martin
    • substitution property: If for each object s of type S  there is an object f of type F such that for all programs P defined in terms of F, the behavior of P is unchanged when f is substituted for s then S is a subtype of T..
      替换原则:对于类型S的任何一个对象s ,都存在一个类型F 的对象f ,在针对F编写的所有程序P中,如果用s替换f后,程序P的“行为”不变,则S是T的子类型(subtype

    LSP是正确设计类层次的指导原则,它检测和保证类层次的正确性,进而维护针对父类型编写的程序的正确性。(你针对父类型,进一步针对抽象类型编程,是OCP的管辖范围)

    多态的本质,是不同子类替代父类变量后,可以对同一种消息,有不同的反应。

    继承的本质,是子类承接了父类的接口。这是语言层面对LSP的支持。当然,程序员刻意用实现违背接口,没有人可以阻挡。




    2.学习思路

    因为易学,所以在《编程导论(Java)》中安排在2.1.1节。所以紧接其后,在[2.1.2 啊,我看到了多态]中才开始介绍向上造型、多态、改写(override);

    所谓难精,我们

    • 继承加以分析,符合LSP的继承有实现继承、拓展继承、接口/协议继承和多继承;参考《4.2.3接口继承 Vs.实现继承》
    • 在介绍接口与实现分离时,强调什么是设计良好的接口,到[4.1.2类的接口]中,说明【p133:如果一个对象能够接受X类的接口的全部操作请求(方法调用),则称对象具有X类型。正是因为Java的子类能够满足父类的接口(尽管可以改写),所以子类的对象能够同时具有类层次中的多个类型】
    • 直到[8.3.2断言的使用指南]中介绍正方形和长方形关系时偷偷摸摸地说:继承关系即Is-A关系,本质上要通过接口的一致来衡量

    实现继承是符合LSP的——即使对具体父类的方法的override有千般不好,它还是符合LSP的。你可以直接使用父类方法,或override空方法。你的方法 m()可以采用改进型override(总是调用super.m()一次)。你在遇到取代型改写时,问一问自己为什么?如果自己能够说服自己,那就用吧!谁敢咬你。

    扩展继承是符合LSP的——即使向上造型导致扩展的方法(子类自己定义的方法)不能够用。白马非马,是好事。既保证了系统的扩展性——如果子类不能够定义自己的新方法,那样的语言你用?同时不影响LSP。比如说,Client依赖的Man接口只有eat(),你超人就算有一万个方法,你造型为Man时你也得憋着。要用子类的方法,(SuperMan)U。多重继承A、B时,你作为A,那么B的方法都被A视为扩展。

    3.正方形和长方形

    很多人使用了“正方形不是一个长方形”这个例子,介绍LSP。事实上,不管你采用什么手段和技巧,我可用反证法告示你:(在例程所示的场景中)如果你能够将正方形设计成长方形的子类,那么你必然可以将圆设计成长方形的子类

    [java] view plaincopy
    1. class Square1 extends Rectangle{  
    2.     private double side;  
    3.     public Square1(double side){  
    4.         this.side= side;  
    5.     }  
    6.     void setSide(double s) {   side = s;    }  
    7.     double getSide() { return side; }  
    8.     @Override double getS() { return side*side;}//求面积  
    9. }  

    4.吐槽(考试的时候,不要随便使用网上的某些文字)

    所谓难精之二,在于五花八门的解释和介绍。yqj2065曾经非常惊讶,这样的里氏替换原则,他还写了设计模式的系列博客。例如,

    [java] view plaincopy
    1.  里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:  
    2. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。  
    3. 子类中可以增加自己特有的方法。  
    4. 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。  
    5. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。  
    LSP仅仅说明可替代性或父子类的接口一致性,实现继承是符合LSP的;实现继承的问题,在于设计时没有更好地封装变化,使得子类不得不override父类的代码。plaincopy
    1.  里氏替换原则,简单地讲就是:子类可以替代父类。它包含以下含义:  
    2. 子类可以实现继承(直接继承或 改变父类原有的功能实现)、拓展继承(扩展父类的功能)、接口/协议继承和多继承。【但是从OCP或封装变化的角度,应该避免override其任何父类中已经实现的方法。】  
    3. 子类中不可退化继承,如鸵鸟不会飞。  
    4. 为了保证父类和子类的接口一致性,要注意:接口包括文档(方法的意图)和方法头。按契约设计(Design by Contract)是一致性判断的良好手段(Java断言机制是工具)。  

    前置条件,通常意味着方法参数的合法性检查(而非重载的类型问题)。如show(Baby b, int age),验证前置条件时,谁会验证出场的人是不是Baby?通常检查它不得为null;而age要大于10,小于16等都是方法文档中已经说明的前置条件,会考虑参数是否double?【子类重载父类的方法时,方法的形参要比父类方法的参数更宽松】,什么意思呢?重载和方法的形参更宽松或狭窄有什么关系?“重载”是敲错了?

    子类的override 方法,返回的可以是父类方法返回类型的子类,Java语法支持。


    又有人写道:“里氏代换原则是实现开闭原则的重要方式之一”。我一直说LSP是OCP的前提条件实现方式Vs. 必要条件 ,我有点傻傻地分不清了快哭了疑问

    你的Client依赖于IServer接口,符合OCP。为什么能够OCP,因为具体的Server可以替代IServer接口。如果(所有的)Server设计的烂,不能够LSP,你能不能OCP?(必要条件)

    但是,我的Client依赖于IServer接口,符合OCP。如果我依赖于具体的Server就不OCP了。好在Server总能够替代IServer,这个LSP好使,所以我的Client就可以依赖于IServer接口,实现了OCP。(实现方式)好像也说得通。

    总体上,这篇面向对象设计原则之里氏代换原则,还不如改为OCP之2。




    展开全文
  • 六大设计原则-里氏替换原则

    千次阅读 2020-03-22 23:51:18
    什么是里氏替换原则里氏替换原则的定义:任何使用基类的地方,都可以透明的使用其子类。从定义中可以看出,它和继承差不多,我们也可以这样理解,里氏替换原则=继承+透明,也就是继承的加强版,取其继承的优点,...

    1、开闭原则       
    2、接口隔离原则
    3、依赖倒置原则
    4、迪米特原则            
    5、里氏替换原则    
    6、单一职责原则

     

    里氏替换原则

    什么是里氏替换原则?

    里氏替换原则的定义:任何使用基类的地方,都可以透明的使用其子类。从定义中可以看出,它和继承差不多,我们也可以这样理解,里氏替换原则=继承+透明,也就是继承的加强版,取其继承的优点,剔除继承的缺点。

    为什么要使用里氏替换原则:

    例如,鸵鸟、燕子从生物学的角度来划分,它们都属于鸟,但从继承关系来看,由于它们不能继承“鸟”会飞的能力,所以它们不能定义成“鸟”的子类。因此如果把鸵鸟和燕子都继承“鸟”的特性,那么在程序中继承类的对象在基类出现的地方就会出现运行错误,为了避免这种错误,我们要遵循里氏替换原则:取消原来的继承关系重新设计它们之间的关系。

    怎么样才算遵守里式替换原则?

    父类实现的东西,子类就不要再写了(就是不要new隐藏);

    如果想修改父类的行为,通过abstract或virtual来修改;

    父类有的,子类必须有,如果出现了子类没有的东西,那么就应该断掉继承,再重新写一个父类

     

     

    展开全文
  • 文章目录什么是里氏替换原则?Why里氏替换原则?里氏抽丝剥茧 什么是里氏替换原则?Why里氏替换原则? 先看看里氏替换原则(Liskov Substitution Principle)的定义: Functions that use pointers or references ...


    什么是里氏替换原则?Why里氏替换原则?

    先看看里氏替换原则(Liskov Substitution Principle)的定义:

    Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

    所有引用基类的地方必须能透明地使用其子类的对象。

    通俗的说,子类可以扩展父类功能,但不能改变父类原有功能。

    在这里插入图片描述

    这是一位女士Barbara Liskov 在1988 年提出——这是最原始的定义:

    “ What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T. ”

    如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 化,那么类型S是类型T的子类型。


    在面向对象的语言里,继承无疑是一个优秀的特性,它有提高代码复用性、扩展性等优点,但是白璧微瑕,它同样也会带来一些不足:
    ● 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
    ● 降低了代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约 束;
    ● 增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大段的代码需要重构。

    为了扬长避短,就有必要引入里氏替换原则。


    里氏抽丝剥茧

    里氏替换原则包含了四层含义:

    * 子类必须完全实现父类的方法

    在做系统设计时,经常会定义一个接口或抽象类,然后编码实现,调用类则直接传 入接口或抽象类,其实这里已经使用了里氏替换原则。

    举个例子来说明这个原则,以CS游戏为例,来描述一下里面用到的枪,类图如图2-1所示:


    2-1:CS游戏中的枪支类图

    在这里插入图片描述


    枪的主要职责是射击,如何射击在各个具体的子类中定义,手枪是单发射程比较近,步枪威力大射程远,机枪用于扫射。在士兵类中定义了一个方法killEnemy,使用枪来杀敌人, 具体使用什么枪来杀敌人,调用的时候才知道。

    枪支抽象类:

    public abstract class AbstractGun {
      //枪用来干什么的?杀敌! 
      public abstract void shoot();
    }
    

    手枪、步枪、机枪的实现类:

    public class Handgun extends AbstractGun { 
       //手枪的特点是携带方便,射程短 
       @Override 
       public void shoot() { 
         System.out.println("手枪射击..."); 
         }
     }
     
    
    public class Rifle extends AbstractGun{ 
      //步枪的特点是射程远,威力大 
      public void shoot(){ 
        System.out.println("步枪射击..."); 
        } 
    }
    
    
    public class MachineGun extends AbstractGun{ 
        public void shoot(){
          System.out.println("机枪扫射...");
         }
    }
    

    士兵的实现类:

    public class Soldier { 
       //定义士兵的枪支 
       private AbstractGun gun; //给士兵一支枪 
       public void setGun(AbstractGun _gun){ 
           this.gun = _gun;
       }
      
       public void killEnemy(){ 
         System.out.println("士兵开始杀敌人..."); 
         gun.shoot();
       }
    
     }
    

    定义士兵使用枪来杀敌,但是这把枪是抽象的,具体是手枪还是步枪需 要在上战场前(也就是场景中)前通过setGun方法确定。场景类Client如下:

    场景类:

    public class Client { 
       public static void main(String[] args) {
          //产生三毛这个士兵 
          Soldier sanMao = new Soldier(); //给三毛一支枪
          sanMao.setGun(new Rifle());
          sanMao.killEnemy(); 
        } 
    }
    

    在这个程序中,给三毛这个士兵一把步枪,然后就开始杀敌了。如果三毛要使用机枪,当然也可以,直接把sanMao.setGun(new Rifle())修改为sanMao.setGun(new MachineGun()) 即可,在编写程序时Solider士兵类根本就不用知道是哪个型号的枪(子类)被传入。

    再来想一想,如果有一个玩具手枪,该如何定义呢?我们先在类图2-1上增加 一个类ToyGun,然后继承于AbstractGun类,修改后的类图如图2-2所示:


    2-2:枪支类图
    在这里插入图片描述

    玩具枪是不能用来射击的,杀不死人的,这个不应该写在shoot方法中。

    public class ToyGun extends AbstractGun { 
      //玩具枪是不能射击的,但是编译器又要求实现这个方法,怎么办?虚构一个呗!  
       @Override 
      public void shoot() { 
      //玩具枪不能射击,这个方法就不实现了
       } 
    }
    

    由于引入了新的子类,场景类中也使用了该类,Client类稍作修改:

    public class Client { 
        public static void main(String[] args) { 
           //产生三毛这个士兵 Soldier sanMao = new Soldier();     
           sanMao.setGun(new ToyGun()); sanMao.killEnemy(); 
         } 
    }
    

    结果:

    士兵开始杀敌人...
    

    玩具枪开始杀人了!在这种情况下,我们发现业务调用类已 经出现了问题,正常的业务逻辑已经不能运行,那怎么办?有两种办法。


    ● 在Soldier类中增加instanceof的判断,如果是玩具枪,就不用来杀敌人。这个方法可以 解决问题,但是,在程序中,每增加一个类,所有与这个父类有关系的类都必须修改,显然,这个方案被否定了。

    ● ToyGun脱离继承,建立一个独立的父类,为了实现代码复用,可以与AbastractGun建 立关联委托关系,如图2-3所示:

    2-3:玩具枪与真实枪分离的类图

    在这里插入图片描述
    例如,可以在AbstractToy中声明将声音、形状都委托给AbstractGun处理,然后两个基类下的子类自由延展,互不影响。


    如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发 生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。


    * 子类可以有自己的个性

    子类可以有自己的行为和外观了,也就是方法和属性—— 里氏替换原则可以正着用,但是不能反过来用。在子类出现的地方,父类未必就可以胜任。还是以刚才的关于枪支的例子为例,步枪有几个比较“响亮”的型号,比如AK47、AUG 狙击步枪等,把这两个型号的枪引入后的Rifle子类图如图2-4所示:


    2-4:增加AK47和AUG后的Rifle子类图
    在这里插入图片描述

    AUG继承了Rifle类,狙击手(Snipper)则直接使用AUG狙击步枪。

    AUG狙击枪类代码:

    public class AUG extends Rifle { 
     //狙击枪都携带一个精准的望远镜
      public void zoomOut(){ 
       System.out.println("通过望远镜察看敌人...");
     }
     public void shoot(){ 
       System.out.println("AUG射击..."); 
     }
     
    }
    

    狙击手类:

    public class Snipper { 
      public void killEnemy(AUG aug){ 
        //首先看看敌人的情况,别杀死敌人,自己也被人干掉 
        aug.zoomOut(); 
        //开始射击 
        aug.shoot();
     } 
    }
    

    狙击手使用狙击枪来杀死敌人,业务场景Client类如下:
    public class Client { 
       public static void main(String[] args) { 
         //产生三毛这个狙击手 
         Snipper sanMao = new Snipper();
         sanMao.setRifle(new AUG()); 
         sanMao.killEnemy(); 
       }
    }
    

    运行结果:

    通过望远镜察看敌人...
    AUG射击...
    

    在这里,系统直接调用了子类,狙击手是很依赖枪支的,别说换一个型号的枪了,就是换一个同型号的枪也会影响射击,所以这里就直接把子类传递了进来。这个时候,我们能不 能直接使用父类传递进来呢?修改一下Client类:

    public class Client { 
      public static void main(String[] args) { 
        //产生三毛这个狙击手 
        Snipper sanMao = new Snipper(); 
        sanMao.setRifle((AUG)(new Rifle()));
        sanMao.killEnemy(); 
       }
    }
    

    显然是不行的,会在运行期抛出java.lang.ClassCastException异常,这也是大家经常说的 向下转型(downcast)是不安全的,从里氏替换原则来看,就是有子类出现的地方父类未必就可以出现。


    * 覆盖或实现父类的方法时输入参数可以被放大

    方法中的输入参数称为前置条件,这是什么意思呢?大家做过Web Service开发就应该知道有一个“契约优先”的原则,也就是先定义出WSDL接口,制定好双方的开发协议,然后再各自实现。里氏替换原则也要求制定一个契约,就是父类或接口,这种设计方法也叫做 Design by Contract(契约设计),与里氏替换原则有着异曲同工之妙。契约制定了,也就同 时制定了前置条件和后置条件,前置条件就是你要让我执行,就必须满足我的条件;后置条 件就是我执行完了需要反馈,标准是什么。这个比较难理解,来看一个例子。

    先定义一个Father类:

    public class Father { 
      //把HashMap转换为Collection集合类型
      public Collection doSomething(HashMap map){
         System.out.println("父类被执行..."); 
         return map.values();
      } 
    }
    

    再定义一个子类:

    public class Son extends Father { 
      //放大输入参数类型 
      public Collection doSomething(Map map){ 
         System.out.println("子类被执行...");
          return map.values();
     } 
    }
    

    子类的doSomething方法与父类的方法名相同,但又不是覆写(Override)父类的方法。方法名虽然相同,但方法的输入参数不同,就不是覆写,是重载(Overload)!——继承,子类拥有父类的所有属性和方法,方法名相同,输入参数类型又不 相同,当然是重载。

    场景类的调用如下:

    public class Client { 
      public static void invoker(){
       //父类存在的地方,子类就应该能够存在 
       Father f = new Father(); 
       HashMap map = new HashMap(); 
       f.doSomething(map); 
      }
      public static void main(String[] args) { 
        invoker();
     }
    }
    

    运行结果:

    父类被执行...
    

    根据里氏替换原则,父类出现的地方子类就可以出现,修改场景类:
    public class Client { 
      public static void invoker(){ 
        //父类存在的地方,子类就应该能够存在 
        Son f =new Son(); 
        HashMap map = new HashMap(); 
        f.doSomething(map);
      }
    
      public static void main(String[] args) { 
        invoker();
      }
      
    }
    

    运行结果还是一样。父类方法的输入参数是HashMap类型,子类的输入参数是Map类型,也就是说子类的输入参数类型的范围扩大了,子类代替父类传递到调用者中,子类的方法永远都不会被执行。这是正确的,如果你想让子类的方法运行,就必须覆写父类的方法。大家可以这样想,在一个Invoker类中关联了一个父类,调用了一个父 类的方法,子类可以覆写这个方法,也可以重载这个方法,前提是要扩大这个前置条件,就是输入参数的类型宽于父类的类型覆盖范围。这样说可能比较难理解,我们再反过来想一 下,如果Father类的输入参数类型宽于子类的输入参数类型,会出现什么问题呢?会出现父类存在的地方,子类就未必可以存在,因为一旦把子类作为参数传入,调用者就很可能进入 子类的方法范畴。

    把上面的例子修改一下,扩大父类的前置条件:

    public class Father { 
        public Collection doSomething(Map map){
          System.out.println("父类被执行..."); 
          return map.values(); 
         }
     }
    

    把父类的前置条件修改为Map类型,再修改一下子类方法的输入参数,相对父类缩小输入参数的类型范围,也就是缩小前置条件:

    public class Son extends Father {
      //缩小输入参数范围 
      public Collection doSomething(HashMap map){
        System.out.println("子类被执行...");
        return map.values();
     } 
    }
    

    在父类的前置条件大于子类的前置条件的情况下,业务场景:

    public class Client { 
      public static void invoker(){ 
        //有父类的地方就有子类
         Father f= new Father(); 
         HashMap map = new HashMap(); 
         f.doSomething(map); 
      }
      
      public static void main(String[] args) { 
       invoker(); 
      } 
    }
    

    运行结果:

    父类被执行...
    

    再把里氏替换原则引入进来——有父类的地方子类就可以使用,把Client类修改一下:

    public class Client { public static void invoker(){ 
      //有父类的地方就有子类 
      Son f =new Son(); 
      HashMap map = new HashMap(); 
      f.doSomething(map);
     }
     public static void main(String[] args) {
       invoker(); 
     } 
    
    }
    

    运行结果:

    子类被执行...
    

    子类在没有覆写父类的方法的前提下,子类方法被执行了,这会引起业务 逻辑混乱,因为在实际应用中父类一般都是抽象类,子类是实现类,你传递一个这样的实现 类就会“歪曲”了父类的意图,引起一堆意想不到的业务逻辑混乱,所以子类中方法的前置条 件必须与超类中被覆写的方法的前置条件相同或者更宽松。

    * 覆写或实现父类的方法时输出结果可以被缩小

    父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一 个类型,要么S是T的子类,为什么呢?分两种情况,如果是覆写,父类和子类的同名方法的 输入参数是相同的,两个方法的范围值S小于等于T,这是覆写的要求,这才是重中之重,子类覆写父类的方法,天经地义。如果是重载,则要求方法的输入参数类型或数量不相同,在 里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这 个方法是不会被调用的,参考上面讲的前置条件。

    采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。



    ⇐⇐ 设计模式—— 一:单一职责原则       

    设计模式—— 三:依赖倒置原则 ⇒⇒



    参考:

    【1】:《设计模式之禅》
    【2】:里氏替换原则(The Liskov Substitution Principle)
    【3】:设计模式六大原则(2):里氏替换原则
    【4】:《大话设计模式》

    展开全文
  • 里氏替换原则是实现开闭原则的重要方式之一。 它克服了继承中重写父类造成的可复用性变差的缺点。 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。 加强程序的健壮性,...
  • 在开始编写文章前,有几个问题需要思考一下:里氏替换原则的庐山真面目里氏替换原则蕴含的规则1....解决方案引入里氏替换原则什么是里氏替换原则呢?它有两种定义:第一种定义,也最正宗的定义:If for each ...
  • 里氏替换原则是我认为其他几个设计原则中比较难以掌握一种,首先他的定义十分晦涩。 假如存在,一个类型T,和已经被实例的对象O 还存在,另一个类型T2,和已经被实例的对象O2 那么存在以下情况,将所有类型为T的对象...
  • 里氏替换原则是实现开闭原则的重要方式之一。 它克服了继承中重写父类造成的可复用性变差的缺点。 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。 加强
  • LSP(里氏替换原则) 两种定义: 类型T类型S的子类型 所用引用基类的地方必须能透明的使用期子类对象 为什么要使用里氏替换原则 让继承中的“利”大于“弊”发挥最大作用,同时减少“弊”所带来的麻烦(继承的...
  • 因为里氏替换原则可以正着用,但是不能反过来用。在子类出现的地方,父类未必就可以胜任。还是以刚才的关于枪支的例子为例,步枪有几个比较“响亮”的型号,比如AK47、AUG狙击步枪等,把这两个型号的枪引入后的...
  • LSP-里氏替换原则

    千次阅读 2016-07-14 11:19:51
    里氏替换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA大会上发表的一篇文章《Data Abstraction and Hierarchy》里面提出来的,主要阐述了有关继承的一些原则,也就是什么时候应该...
  • 里氏替换原则(Liskov Substitution Principle,LSP),知识点包括:1、什么是里氏替换原则?2、为什么需要遵守里氏替换原则? 3、怎么实现里氏替换原则,保证子类能透明的替换父类?3、里氏替换原则(LSP)与开闭...
  • 1.里氏替换原则是实现开闭原则的重要方式之一 2.它克服了继承中重写父类造成的可复用性差的缺点 3.它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。 4.加强程序的健壮性,...
  • 里氏替换原则

    2011-04-17 23:25:00
    原文出自 ...里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA大会上发表的一篇文章《Data Abstraction and
  • 里氏替换原则的理解: ...1.里氏替换原则要实现的逻辑: 声明对象时用基类/接口来声明,具体对象的实现 用new不同的实体来达到实现不同类中的功能的目的,就是向下转型。 2.对于为什么当子类的方法重载
  • Liskov Substitution Principle 里氏替换原则 定义:如果对每一类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2类型T1的子类型...
  • 设计模式七大原则之里氏替换原则 一、继承 1.1 继承的含义 父类中凡是已经实现好的方法, 实际上在设定规范和契约, 虽然他不强制要求所有的子类必须...二、什么是里氏替换原则 所有引用父类的地方必须能透明地

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 394
精华内容 157
关键字:

里氏替换原则是什么