Java设计原则 - 里氏替换原则

六大设计原则

定义

如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

看不懂,没关系,还有一种定义

所有引用基类的地方必须透明的使用其子类的对象

还是看不懂,没关系。

简单的来说,T1是一个基类,T2是T1的子类,所有使用T1的地方,都代替为T2之后,程序行为没有发生改变。这下应该懂了吧。

里氏替换原则其实就是为“良好的继承”制定一些规范。

规范

1. 子类要完全实现父类的抽象方法,但尽量不要覆盖父类的非抽象方法

这一点很容易理解,如果子类覆盖了父类的非抽象方法,当使用子类代替父类时,程序行为可能会有所改变。

举个例子:
父类

public class Calculator {
    public int add(int num1, int num2) {
        return num1 + num2;
    }
}
public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        calculator.add(10, 5);
        // 结果为15
    }
}

子类

public class MiniCalculator extends Calculator {
    public int add(int num1, int num2) {
        return num1 - num2;
    }
}
public class Client {
    public static void main(String[] args) {
        // 子类代替父类
        // Calculator calculator = new Calculator();
        MiniCalculator calculator = new MiniCalculator();
        calculator.add(10, 5);
        // 结果为5
    }
}

例子中,由于子类MiniCalculator重写了父类Calculator的add方法,当子类MiniCalculator代替父类Calculator时,导致计算结果有误,也就是程序的行为发生了变化。所以对父类的非抽象方法,尽量不要覆盖重写。

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

子类一般都会有自己特有的属性或方法,这点是肯定的。

3.当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数要更抽象化。

当我们要覆盖重载父类非抽象方法时,为了确保父类的方法能被正确执行,方法的形参应该更抽象化。

举个例子:
父类

public class Father{
    public Collection doSomething(HashMap map){
        System.out.println("父类被执行");
        return map.values();
    }
}

子类

public class Son extends Father{
    public Collection doSomething(Map map){
        System.out.println("子类被执行");
        return map.values();
    }
}

调用父类执行:

public class Client {
    public static void main(String[] args) {
        Father father = new Father();
        HashMap map = new HashMap();
        father.doSomething(map);
        // 结果是父类被执行
    }
}

当我们用子类代替父类时:

public class Client {
    public static void main(String[] args) {
        // 子类代替父类
        // Father father = new Father();
        Son son = new Son();
        HashMap map = new HashMap();
        son.doSomething(map);
        // 结果是还父类被执行
    }
}

结果是一样的,由于传入的是一个HashMap类型,所以由父类去执行,子类重写等于没写,不知道这样的重写有啥意义。

4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更具体化

父类的一个方法的返回值是一个类型T,子类的相同方法的返回值为S,那么里氏替换原则就要求S必须小于等于T。

小结

其实上面的规范,一般人(除了不小心)都不会违反,因为那样做没有意义!!所以说了等于白说!

但是告诉了我们一个想法,如果我们遵守了里氏替换原则,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。这样的话,在一定程度上增强程序的健壮性。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值