• SOLID设计原则


    一、里氏替换原则

    里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理

    1.1 作用

    • 里氏替换原则是实现开闭原则的重要方式之一
    • 它克服了继承中重写父类造成的可复用性变差的缺点
    • 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性
    • 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险

    1.2 实现方法

    通俗理解:只要有父类出现的地方,都可以用子类来替代,里氏替换原则对继承进行了规则上的约束,有以下4个方面

    1. 子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法
    2. 子类中可以增加自己特有的方法
    3. 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
    4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格

    1.3 应用实例

    子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法

    ⚠️ 如果子类覆写了父类中的非抽象方法会带来什么后果?

    public class LSPtest {
        public static void main(String[] args) {
            Bird bird1 = new Swallow();
            Bird bird2 = new BrownKiwi();
            bird1.setSpeed(120);
            bird2.setSpeed(120);
            System.out.println("如果飞行300公里:");
            try {
                System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");
                System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");
            } catch (Exception err) {
                System.out.println("发生错误了!");
            }
        }
    }
    //鸟类
    class Bird {
        double flySpeed;
        public void setSpeed(double speed) {
            flySpeed = speed;
        }
        public double getFlyTime(double distance) {
            return (distance / flySpeed);
        }
    }
    
    //燕子类
    class Swallow extends Bird {}
    
    //几维鸟类
    class BrownKiwi extends Bird {
        @Override
        public void setSpeed(double speed) {
            flySpeed = 0;
        }
    }
    

    ️ 上述实例代码中,BrownKiwi没有翅膀不会飞,导致程序执行发生错误,违背了历史替换原则,正确做法应该是定义BirdBrownKiwi的更一般的类animalBird extends AnimalBrownKiwi extends AnimalSwallow extends Bird,并重新定义方法(例如动物都有runTime、runSpeed)。

    子类中可以增加自己特有的方法

    当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松,这样才满足里氏替换原则

    正确示例

    public class A {
        public void fun(HashMap map) {
            System.out.println("父类被执行...");
        }
    }
    
    class B extends A {
        public void fun(Map map) {
            System.out.println("子类被执行...");
        }
    }
    
    // 子类的方法参数Map比父类的HashMap参数更宽松,满足里氏替换原则,只有父类的fun方法会被执行
    class demo {
        public static void main(String[] args) {
            A a = new A();
            HashMap<Integer, Integer> map = new HashMap();
            a.fun(map); // 父类被执行...
            B b = new B(); //父类存在的地方,可以用子类替代,子类B替代父类A
            b.fun(map); // 父类被执行...
        }
    }
    

    ❓ 此时可能会有一个疑问,上述子类B代码中fun(Map map)函数什么时候调用呢?

    A a = new A();
    Map<Integer, Integer> map = new HashMap();
    B b = new B();
    b.fun(map); // 子类被执行...
    // 此时如果调用a.fun(map); 编译器会报错
    

    错误示例

    public class A {
        public void fun(Map map) {
            System.out.println("父类被执行...");
        }
    }
    
    class B extends A {
        public void fun(HashMap map) {
            System.out.println("子类被执行...");
        }
    }
    
    // 子类的方法参数Map比父类的HashMap参数更宽松,不满足里氏替换原则
    // 两次调用fun(),分别执行了父类和子类的函数,引起了程序逻辑的混乱
    class demo {
        public static void main(String[] args) {
            A a = new A();
            HashMap<Integer, Integer> map = new HashMap();
            a.fun(map); // 父类被执行...
            B b = new B(); //父类存在的地方,可以用子类替代,子类B替代父类A
            b.fun(map); // 子类被执行...
        }
    }
    

    当子类的方法实现父类的(抽象)方法时,方法的后置条件(即方法的返回值)要与父类一致或更严格

    public abstract class A {
        public abstract Map fun();
    }
     
    class B extends A{
        @Override
        public HashMap fun(){
            HashMap b=new HashMap();
            b.put("b","子类被执行...");
            return b;
        }
    }
    
    class demo {
        public static void main(String[] args){
            A a=new B();
            System.out.println(a.fun());
        }
    }
    

    ️ 里氏替换原则不是不能override,而是要按照父类所期望的那样去override。这告诉我们,继承也是可以使用的,不过要熟悉相关说明。

  • 相关阅读:
    拓端tecdat|R语言ARIMA、ARIMAX、 动态回归和OLS 回归预测多元时间序列
    拓端tecdat|python贝叶斯随机过程:马尔可夫链MarkovChain,MC和MetropolisHastings,MH采样算法可视化
    拓端tecdat|R语言Kmeans和层次聚类分析癌细胞系微阵列数据和树状图可视化比较
    拓端tecdat|R语言用有限混合模型(FMM,finite mixture model)创建衰退指标对股市SPY、ETF收益聚类和双坐标图可视化
    php/nginx/axios: 文件上传: 配置最长执行时间等相关项 (php8.1.1 / nginx 1.18.0 / vue@3.2.26 )
    thinkphp6: 生成zip压缩包并下载(php 8.1.1 / thinkphp v6.0.10LTS )
    thinkphp6: 给接口api做签名验证(php 8.1.1 / thinkphp v6.0.10LTS )
    thinkphp6: 用imagemagick库生成缩略图(ImageMagick 6.9.1160 / php 8.1.1 / thinkphp v6.0.10LTS )
    thinkphp6: 用phpspreadsheet导出数据到excel (php 8.1.1 / thinkphp v6.0.10LTS / phpspreadsheet 1.21.0 )
    vue.js3: 从详情页返回列表页,显示跳转前列表而不是第一页(vue@3.2.26)
  • 原文地址:https://www.cnblogs.com/ffopen/p/15550168.html
Copyright © 2020-2023  润新知