面向对象设计原则 ---- 里氏替换原则(LSP)

用户头像
张荣召
关注
发布于: 2020 年 09 月 27 日

Liskov替换:  OCP的关键:抽象。抽象的威力在于多态和继承。

  • 正确的继承-------符合里氏替换原则。

  • 简言:子类型必须能够替换掉他们的基类性。



里氏替换原则的核心:

       不是静态分析两个对象之间知否有继承关系,而是在应用场景,验证子类能不能替换父类。

       如果替换后,能正常运行=>合理的继承。

       如果不能正常运行=>不合理的继承。

       静态视角:垂直层面,使用抽象向下扩展子类----------------------IS-A静态关系。

       动态视角:水平层面,放到应用环境上下文,验证继承的正确性。



举例:验证白马,黑马,小马的继承正确性。

 

应用上下文:人骑马-----------将马的任意对象,来替换到应用场景。

                    人骑白马---------正确

                    人骑黑马---------正确

                    人骑小马---------错误------不合适的继承。 

                    在使用Horse对象的任何场合,我们可以把WhiteHorse对象传递进去,以取代Horse对象,程序仍然正确。

验证方法:在应用场景中,使用子类替换父类,验证程序能不能正常运行。



案例二:评估是否符里氏替换原则?

void drawShape(Shape shape){

      if(shape instanceof Circle){  drawCircle((Circle)shape);}

      else if(shape instanceof Square){drawSquare((Square)shape);}

      else { .... }

}

评估方法:将Shape的任意子类,传递进去,程序都能正常运行。

验证1:传递子类Circle(实现Shape)====运行正常====正确。

验证2:传递子类Square(实现Shape)  ====运行正常====正确。

验证3:传递子类三角形 (实现Shape)====无法正常处理===违反LSP。

改进方法:

void drawShape(Shape shape){ shape.draw();}



案例3:“正方形” is-a “长方形”吗?

public class Rectangle{

      private double width;

      private double height;

      public void setWidth(double w){this.width=w;}

      public double getWidth(){return this.width;}

      public double getHeight(){return this.height;}

      public void setHeight(double h){this.height=h;} 

}

正方形可以继承正方形吗?

public class Square extends Rectangle{

      public void setWidth(double w){this.width=height=w;}

      public void setHeight(double h){this.height=width=h;}

}



假如测试方法:

void testArea(Rectangle rect){   //动态分析:传入正方形,断言失败。

     rect.setWidth(3);

     rect.setHeight(4);   

     assert(12==rect.calculateArea());//传入正方形将失败。

}



解析:静态分析,Square继承Rectangle没有问题。

          动态分析(里氏替换分析应用场景):

                  1.传入长方形,断言成功,面积计算正确 ,

                  2.传入正方形,断言失败,面试计算错误

                     ===>正方形不是长方形。

                     ===> Square extends Rectangle就是不合理的继承。



LSP要求:凡是使用基类的地方,一定也适用于其子类。

Java语法角度看:

  • 子类一定得拥有基类的整个接口。

  • 子类的访问控制不能比基类更严格。

子类的“契约"不能比基类更”严格“。



               



用户头像

张荣召

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
面向对象设计原则----里氏替换原则(LSP)