设计模式六大原则(二)-里氏替换原则

242 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

里氏替换原则是指导我们合理使用继承的思想,在这之前得理解一下继承和多态。

继承、多态

优点

  • 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性
  • 提高代码的可复用性

缺点

当被继承的父类为一个可实例化的类的时候,往往会出现如下缺点

  • 继承是侵入性的。子类会破坏父类,会修改父类得原有逻辑,使得调用者得“小心翼翼”
  • 降低代码的灵活性。子类拥有父类得所有属性和方法,想要实现复用,必须考虑父类的属性或方法
  • 增强了耦合性。当子类的实现依赖于父类的属性(常量、方法),那么当父类发生修改时必须考虑子类的逻辑

多态的具体表现

多态的具体表现为,父类的引用可以指向子类的实例。

public class PersonBase {
    //在程序员A得眼里,所有人都喜欢漂亮得女孩子
    void like(){
        System.out.println("喜欢漂亮的女孩子");
    }
}
class PersonA extends PersonBase {
    @Override
    void like() {
        super.like();
    }

}
class PersonB extends PersonBase {
    @Override
    void like() {
        super.like();
    }
}
@Test
public void test(){
    PersonBase personA = new PersonA();
    PersonBase personB = new PersonB();
    personA.like();
    personB.like();
}

此刻使用父类的引用指向子类的实例,不会修改原有程序逻辑。

若此刻我们添加一个实现类Personc ,喜欢可爱的女孩子,那么此刻使用父类的引用指向子类的实例就会修改程序的逻辑。那么Personc 的实现是不符合里氏替换原则的。

class PersonC extends PersonBase {
    @Override
    void like() {
        System.out.println("喜欢可爱的女孩子");
    }
}

重写

子类重写父类方法有以下规则

  1. 返回值类型、方法名、参数列表必须相同,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
  2. 如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法。
  3. 构造方法无法被重写

方法的重写要遵循“两同两小一大”

  • “两同”即方法名相同、形参列表相同;
  • “两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
  • “一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。

里氏替换原则(LSP)

有两条定义:

  • 如果S是T的子类,则T的对象可以替换为S的对象,而不会改变程序原有逻辑。
  • 所有引用其父类对象方法的地方,都可以透明的替换为其子类对象

不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。

继承和多态是面向对象语言所提供的一种语法,是代码实现的思路,而里式替换则是一种思想,一种设计原则,是用来指导继承关系中子类该如何设计,从而保证程序的可扩展性和健壮性。

里氏替换原则的作用

  1. 克服了继承中重写父类造成的可复用性变差的缺点。
  2. 指导使用继承多态,增加程序健壮性、可复用性、兼容性。

里氏替换原则的实现方法

尽量不要从可实例化的父类中继承,而是要使用基于抽象类和接口的继承。父类用于写通用方法,子类用于扩展父类的功能,但不能改变父类原有的功能。