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

探索神秘未知

主目录:一个面向对象设计(OOD)的学习思路设计bash

引子:学习

有一只小麻雀在大平原上,飞呀飞~。飞累了,看见前方一个大鸟... 小麻雀:大鸟兄你好,本鸟叫麻雀!请问您怎么称呼? 大鸵鸟:原来是麻雀小弟呀!本鸟叫鸵鸟! 小麻雀:鸵鸟哥耶!小弟飞的累的不行!让兄弟在您雄伟的身躯上歇歇脚么? 大鸵鸟:不行!本鸟还走累了呢!那我咋办? 小麻雀:你飞呗!难道我还拖着你不成? 大鸵鸟:前提是我要是能飞的起来呀! 小麻雀:开什么玩笑!我们都是鸟,你飞不起来?“飞”是咋们鸟类的特征,想到飞就想到咋们鸟~。ui


LSP.png

1. 何为LSP?

  • 全称:里氏替换原则(Liskov Substitution principle)
  • 定义:派生类(子类)对象可以替换其基类(超类)对象被使用^foot1
  • Barbara Liskov对LSP定义是这么说的:若对每一个类型S的对象q1,都存在一个类型T的对象q2,使得在全部对T编写的程序P中,用q1替换q2后,程序P行为功能不变,则ST的子类型。 听着有些绕,我将它画一个类图便于理解:
    LSP定义理解dsf
在类P中将T的对象q2,换成S的对象q1行为功能不变
则S继承T,得如图所示的关系

2. 何为L?何为S?

L:芭芭拉·利斯科夫(Barbara Liskov)由于提出这个原则的女士姓里 S:替换(Substitution)父类能被子类替换this

  • 替换如上述定义所述,子类替换父类后不会影响其行为和功能。

3. 为什么要有LSP?

①首先谈谈要是违反LSPspa

  • 来张违反LSP的类图

违反LSP.png

  • 分析.net

  • 如今我说天上飞着一只鸟。。。设计

  • 子类麻雀替换父类:天上飞着一只麻雀。code

  • 子类鸵鸟替换父类:天上飞着一只鸵鸟。对象

  • 由上由于违反了里氏替代原则,致使整个设计存在严重逻辑错误。blog

  • 因为违反了里氏替代原则,间接的违反了OCP原则^foot2。由于明显能够看出飞翔对于鸵鸟因该是封闭的。

②再来看一些代码(LSP的违反致使OCP的违反)

  • 代码以下

有三个类:鸟、鸵鸟、麻雀。鸵鸟和麻雀都有要去北京的方法

/**
 * 鸟
 */
class Bird{
    public static final int IS_OSTRICH = 1;//是鸵鸟
    public static final int IS_SPARROW = 2;//是麻雀 
    public int isType;
    public Bird(int isType) {
        this.isType = isType;
    }
}
/**
 * 鸵鸟
 */
class Ostrich extends Bird{
    public Ostrich() {
        super(Bird.IS_OSTRICH);
    }
    public void toBeiJing(){
        System.out.print("跑着去北京!");
    }
}

/**
 * 麻雀
 */
class Sparrow extends Bird{
    public Sparrow() {
        super(Bird.IS_SPARROW);
    }
    public void toBeiJing(){
        System.out.print("飞着去北京!");
    }
}

复制代码

如今有一个方法birdLetGo,统一处理去北京的行为

public void birdLetGo(Bird bird) {
        if (bird.isType == Bird.IS_OSTRICH) {
            Ostrich ostrich = (Ostrich) bird;
            ostrich.toBeiJing();
        } else if (bird.isType == Bird.IS_SPARROW) {
            Sparrow sparrow = (Sparrow) bird;
            sparrow.toBeiJing();
        }
    }
复制代码
  • 分析 你们能够看出,birdLetGo方法明显的违反了开闭原则^foot2,它必需要知道全部Bird的子类。而且每次建立一个Bird子类就得修改它一次。

③结论

由上面的分析能够大体的了解了遵照LSP的重要性了吧!

  • 若是不遵照,致使逻辑设计缺陷
  • 若是不遵照,致使同时违反开闭原则
  • 单个模型,孤立时并不具备设计意义。当多个模型出现时,抽象提取共同特征做为父类(基类),使之任何子类能替代于父类
  • 若是试图预测全部假设,咱们所获得的结果可能会充满不少没必要要的复杂性。一般最好的办法是只预测那些最明显的LSP的违反状态,直到设计开始出现脆弱的状态,才去处理它们。[^foot3]

4. 基于契约设计能支持LSP?

  • 什么是契约设计?
  • 经过为每一个方法声明的前置条件和后置条件[^foot4]来指定的。要是使一个方法得以执行,前置条件必需要为真。执行完毕后,该方法要保证后置条件为真。
  • 一个例子

几个继承关系的类

//动物
public class Animal {
    private String food;
    public Animal(String food) {
        this.food = food;
    }
    public String getFood() {
        return food;
    }

}

//鸟
class Bird extends Animal{
    public Bird(String food) {
        super(food);
    }
}

//鸵鸟
class Ostrich extends Bird{
    public Ostrich() {
        super("草");
    }
}

//麻雀
class Sparrow extends Bird{
    public Sparrow() {
        super("虫子");
    }
}

复制代码

在动物园对象中调用吃的方法

class Zoo {
    /**
     * 吃早餐
     */
    public String eatBreakfast(Animal animal) {
        return animal.getFood();
    }
}
复制代码

分析

  • 这里的知足前置条件就是调用方需知足能接受String这个食物类型
  • 知足后置条件能够看作是参数和返回类型
  • 前置条件不能更强,只能更弱,好比能够这样调用:
Object food = new Zoo().eatBreakfast(new Animal("肉"));
复制代码
  • 后置条件能够更强,好比能够这样写:
String food = new Zoo().eatBreakfast(new Ostrich());
复制代码
  • 这样咱们就能够说是前置条件和后置条件就都得以知足

5. 结论总结[^foot3]

  • 若是LSP有效运用,程序会具备更多的可维护性、可重用性和健壮性

  • LSP是使OCP成为可能的主要原则之一

  • 正是由于子类的可替换性,才使得父类模块无须修改的状况就得以扩展

6. 参考文章

[^foot3]: 敏捷软件开发 第10章 里氏替换原则(LSP) [^foot4]: 前置条件和后置条件是什么?

相关文章
相关标签/搜索