设计模式之七大原则-里氏替换原则(Liskov Substitution Principle)

1.基本介绍

  • 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的姓里的女士提出的
  • 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所以程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说:所有引用基类的地方必须能透明地使用其子类的对象
  • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
    -继续实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题

2.应用案例

  • 采用继承方式覆盖父类的方法

    public class LiskovSubstitution {
        public static void main(String[] args) {
            A a = new A();
            System.out.println("11-3=" + a.func1(11, 3));
            System.out.println("1-8=" + a.func1(1, 8));
    
            System.out.println("-----------------------");
            B b = new B();
            System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出11-3
            System.out.println("1-8=" + b.func1(1, 8));// 1-8
            System.out.println("11+3+9=" + b.func2(11, 3));
        }
    }
    
    // A类
    class A {
        // 返回两个数的差
        public int func1(int num1, int num2) {
            return num1 - num2;
        }
    }
    
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends A {
        //这里,重写了A类的方法, 可能是无意识
        public int func1(int a, int b) {
            return a + b;
        }
    
        public int func2(int a, int b) {
            return func1(a, b) + 9;
        }
    }	
    

    运行结果:

    11-3=8
    1-8=-7
    -----------------------
    11-3=14
    1-8=9
    11+3+9=23
    

     由运行结果可看出,由于B继承A并且重写了父类(A)的func1方法,造成所有运行相减功能的代码全部调用了类B重写后的方法,造成原本运行正常的功能出现了错误。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

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

  • 改进后的代码

    public class LiskovSubstitution {
        public static void main(String[] args) {
            A a = new A();
            System.out.println("11-3=" + a.func1(11, 3));
            System.out.println("1-8=" + a.func1(1, 8));
    
            System.out.println("-----------------------");
            B b = new B();
            //因为B类不再继承A类,因此调用者,不会再func1是求减法
            //调用完成的功能就会很明确
            System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出11+3
            System.out.println("1+8=" + b.func1(1, 8));// 1+8
            System.out.println("11+3+9=" + b.func2(11, 3));
    
    
            //使用组合仍然可以使用到A类相关方法
            System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
        }
    }
    
    //创建一个更加基础的基类
    class Base {
        //把更加基础的方法和成员写到Base类
    }
    
    // A类
    class A extends Base {
        // 返回两个数的差
        public int func1(int num1, int num2) {
            return num1 - num2;
        }
    }
    
    // B类继承了A
    // 增加了一个新功能:完成两个数相加,然后和9求和
    class B extends Base {
        private A a = new A();
    
        //重写A类的方法
        public int func1(int a, int b) {
            return a + b;
        }
    
        public int func2(int a, int b) {
            return func1(a, b) + 9;
        }
    
        //我们仍然想使用A的方法
        public int func3(int a, int b) {
            return this.a.func1(a, b);
        }
    }
    

    修改后,我们通过B类使用其父类(A)原有的方法,这样就不会出现错误

3.注意事项和细节

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值