里氏替换原则(Liskov Substitution principle)

在软件设计、编码过程中有几个基本原则即SOLID原则,学习理解能够帮忙我们写出更健壮的代码。SOLID是五个基本原则的首字母。这五个原则如下:

  • Single responsibility

  • Open–closed

  • Liskov substitution

  • Interface segregation

  • Dependency inversion

此篇来学习一下里氏替换原则(Liskov Substitution responsibility)

那么什么是里氏替换原则(LSP)呢?

Barbara Liskov 和 Jeannette Wing 在1994年的原文中是这么定义的:

Subtype Requirement: Let be a property provable about objects of type T. Then

should be true for objects of type S where S is a subtype of T.

原谅我蹩脚的英文,我个人理解意思就是:对于类型T的属性x可以推导出f(x),如果S是T的子类型,并且对于类型S的属性y有f(y)成立。

再引申一点讲就是:如果对于每一个类型为T的对象m,都有类型为S的对象n,满足以T定义的所以程序将对象m替换成n的时候,程序的行为没有改变,即T是S的父类,且子类可以替换父类。看上去好像挺好理解的,好像子类继承自父类,那么为什么不能替换呢,又不会报ClassCastException。然而(but),看官请往下看…

我们照例拿代码来举个反例例子(下面是我从网上找到的比较经典的例子):

矩形(Rectangle):

/**
 * 矩形有两个属性,长(length)和宽(breadth)
 */
public class Rectangle {
    private int length;
    private int breadth;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getBreadth() {
        return breadth;
    }

    public void setBreadth(int breadth) {
        this.breadth = breadth;
    }

    public int getArea() {
        return this.length * this.breadth;
    }
}

正方形(Square):

/**
 * 正方形,正方形本来只有边长,但是为了便于理解我们直接Override了父类的方法
 */
class Square extends Rectangle {
    @Override
    public void setBreadth(int breadth) {
        super.setLength(breadth);
        super.setBreadth(breadth);
    }
    @Override
    public void setLength(int length) {
        super.setLength(length);
        super.setBreadth(length);
    }
}

那么我们来做个小测试:

public class LspTest
{
    private static Rectangle getNewRectangle()
    {
        // it can be an object returned by some factory ...
        // 返回Square实例
        return new Square();
    }

    public static void main (String args[])
    {
        Rectangle r = LspTest.getNewRectangle();

        r.setBreadth(5);
        r.setLength(10);
        // user knows that r it's a rectangle.
        // 用户知道r是一个Rectangle类的实例
        // It assumes that he's able to set the width and length as for the base class
        // r实例可以设置width和length

        System.out.println((r.getClass().getName()));
        System.out.println(r.getArea());
        // now he's surprised to see that the area is 100 instead of 50.
        // 输出结果让人惊讶,本来觉得应该是Rectangle和50,但是却是Rectangle和100,
        // 所以这种情况不满足里氏替换原则
    }
}

运行一下,输出的结果竟然是Rectangle和100,而不是Rectangle和50。

So,大家在设计类的体系结构的时候,一定要注意哈。举一个OK的例子:

比如今年公司年会,对于抽奖环节,全员参与。Boss可以抽奖、开发汪可以、运维汪也可以,QA小妹也可以,那么就是满足里氏替换原则的。祝大家年会都能抽中大奖!

参考《涉及模式之禅》里面的一句话:在项目中,采用里氏替换原则的时候,应该尽量避免子类的“个性”,一旦子类有“个性“,这个子类和父类之间的关系就很难调和了。

最后,欢迎大家批评指正哈!

参考:

https://en.wikipedia.org/wiki/Liskov_substitution_principle

http://blog.csdn.net/u011288271/article/details/52497602

https://www.oodesign.com/liskov-s-substitution-principle.html

你可能感兴趣的:(设计模式,里氏替换原则)