• 03 里氏替换原则


    官方定义:

    里氏替换原则(Liskov Substitution Principle,LSP)是由麻省理工学院计算机科学系教授芭芭拉·利斯科夫于 1987 年在“面向对象技术的高峰会议”(OOPSLA)上发表的一篇论文《数据抽象和层次》(Data Abstractionand Hierarchy)里提出的.
    她在论文中提到:如果S是T的子类型,对于S类型的任意对象,如果将他们看作是T类型的对象,则对象的行为也理应与期望的行为一致

    子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。

    如何理解里氏替换原则

    要理解里氏替换原则,其实就是要理解两个问题:
    什么是替换?
    什么是与期望行为一致的替换(Robert Martin所说的“必须能够替换”)?

    1 ) 什么是替换 ?
    替换的前提是面向对象语言所支持的多态特性,同一个行为具有多个不同表现形式或形态的能力。
    在这里插入图片描述
    2 ) 什么是与期望行为一致的替换?
    在不了解派生类的情况下,仅通过接口或基类的方法,即可清楚的知道方法的行为,而不管哪种派生类的实现,都与接口或基类方法的期望行为一致。

    不需要关心是哪个类对接口进行了实现,因为不管底层如何实现,最终的结果都会符合接口中关于方法的描述(也就是与接口中方法的期望行为一致).
    或者说接口或基类的方法是一种契约,使用方按照这个契约来使用,派生类也按照这个契约来实现。这就是与期望行为一致的替换。

    场景案例

    比如在一个商城项目中,有3种促销活动:
    1)PromotionalStrategy (满减活动,两百以上百八折)
    2)RebateStrategy(打折活动)
    3)ReduceStrategy(返现活动)
    在这里插入图片描述

    public interface Istrategy {
    	public double realPrice(double consumePrice);
    }
    public class PromotionalStrategy implements Istrategy {
    	public double realPrice(double consumePrice) {
    if (consumePrice > 200) {
    return 200 + (consumePrice - 200) * 0.8;
    } else {
    return consumePrice;
    }
    }
    }
    public class RebateStrategy implements Istrategy {
    private final double rate;
    public RebateStrategy() {
    this.rate = 0.8;
    }
    public double realPrice(double consumePrice) {
    return consumePrice * this.rate;
    }
    }
    public class ReduceStrategy implements Istrategy {
    public double realPrice(double consumePrice) {
    if (consumePrice >= 1000) {
    return consumePrice - 200;
    } else {
    return consumePrice;
    }
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    调用方为Context,在此类中使用接口定义了一个对象

    public class Context {
    //使用基类定义对象变量
    private Istrategy strategy;
    // 注入当前活动使用的具体对象
    public void setStrategy(Istrategy strategy) {
    this.strategy = strategy;
    }
    // 计算并返回费用
    public double cul(double consumePrice) {
    // 使用具体商品促销策略获得实际消费金额
    double realPrice =
    this.strategy.realPrice(consumePrice);
    // 格式化保留小数点后1位,即:精确到角
    BigDecimal bd = new BigDecimal(realPrice);
    bd = bd.setScale(1, BigDecimal.ROUND_DOWN);
    return bd.doubleValue();
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Context 中代码使用接口定义对象变量,这个对象变量可以是实现了lStrategy
    接口的PromotionalStrategy、RebateStrategy 、 ReduceStrategy任意一个。

    里氏代换原则与多态的区别 ?

    虽然从定义描述和代码实现上 来看,多态和里式替换有点类似,但它们关
    注的角度是不一样的。多态是面向对象编程的一 大特性,也是面向对象编
    程语言的一种语法。它是一种代码实现的思路。而里式替换是一种 设计原
    则,用来指导继承关系中子类该如何设计,子类的设计要保证在替换父类
    的时候,不 改变原有程序的逻辑及不破坏原有程序的正确性。

    里氏替换原则和依赖倒置原则,构成了面向接口编程的基础,正因为里氏替换
    原则,才使得程序呈现多样性。

  • 相关阅读:
    java数据结构(红黑树)set集合 HashSet HashSet三个问题 LinkedHashSetTreeSet TreeSet集合默认规则排序规则
    springboot升级过程中踩坑定位分析记录 | 京东云技术团队
    详解预处理(2)
    基于Vue3+TS的Monorepo前端项目架构设计与实现
    C#:Winfrom 实现DataGridView 自定义分页
    python 获取本机ip
    写给Java/Android开发者的Python入门教程
    vue3,使用watch监听props中的数据
    windows消息-键盘消息
    第一百五十九回 SliverAppBar组件
  • 原文地址:https://blog.csdn.net/weixin_39563769/article/details/133898982