本文介绍: 里氏代换原则()面向对象设计基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换基类软件单位功能不受到影响时,基类才能真正被复用,而衍生类也能够基类的基础上增加新的行为里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤规范

一.定义

        里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤规范。 

        总的来说,该原则的核心思想就是程序当中,如果将一个父类对象替换成它的子类对象后,该程序不会发生异常。这也是该原则希望达到的一种理想状态

通俗的来讲就是:子类可以扩展父类功能,但不能改变父类原有的功能

二.原理

        换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别

        比如假设两个类,一个是Base类,另一个是Child类,并且Child类是Base的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b)那么它必然可以接受一个子类的对象method1(Child c).

        里氏替换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才能真正的被复用,而衍生类也才能够在基类的基础上增加新的行为

        但是需要注意的是,反过来的代换是不能成立的,如果一个软件实体使用的是一个子类的话,那么它不一定适用于基类。如果一个方法method2接受子类对象为参数的话method2(Child c),那么一般而言不可以有method2(b).

里氏替换原则通俗的来讲就是:子类可以扩展父类功能,但不能改变父类原有的功能。它包含以下4层含义:

不遵循里氏替换原则的后果:

public class A{
    public int func1(int a, int b){
        return a-b;
    }
}
public class B extends A{  
    public int func1(int a, int b){  
        return a+b;  
    }  

    public int func2(int a, int b){  
        return func1(a,b)+100;  
    }  
} 

 

package com.designphilsophy.lsp.version3;

public class Client{  
    public static void main(String[] args){  
        B b = new B();  
        System.out.println("100-50="+b.func1(100, 50));  
        System.out.println("100-80="+b.func1(100, 80));  
        System.out.println("100+20+100="+b.func2(100, 20));  
    }  
}  

运行结果如下

100-50=150
100-80=180
100+20+100=220

        错误原因就是类B在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类B重写后的方法,造成原本运行正常的功能出现错误。在本例中,引用基类A完成的功能,换成子类B之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。 

 

三.典例 

        中学时期的数学课告诉我们正方形是一种特殊的长方形,只不过是它的长和宽是一样的,也就是说我们在面向对象里我们应当将长方形设计成父类,将正方形设计成长方形的子类,但是实际上,这样做是错误的,并不符合里氏替换原则~

public class Rectangle {
    protected long width;
    protected long height;

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

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

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

    public long getHeight() {
        return this.height;
    }
}
public class Square extends Rectangle {
    public void setWidth(long width) {
        this.height = width;
        this.width = width;
    }

    public long getWidth() {
        return width;
    }

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

    public long getHeight() {
        return height;
    }
}
public class SmartTest
{
    /**
     长方形的长不短的增加直到超过宽
     */
    public void resize(Rectangle r)
    {
        while (r.getHeight() <= r.getWidth() )
        {
            r.setHeight(r.getHeight() + 1);
        }
    }
}

        在上边的代码中我们定义了一个长方形和一个继承自长方形的正方形,看着是非常符合逻辑的,但是当我们调用SmartTest类中resize方法时,长方形是可以的,但是正方形就会一直增大,一直long溢出按照里氏替换原则,父类可以的地方,换成子类一定也可以,所以上边的这个例子不符合里氏替换原则的

问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。 

 解决方式是:可以为长方形和正方形规定一个共同的基类~

原文地址:https://blog.csdn.net/jsl123x/article/details/134699813

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_49020.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注