里氏替换原则

所有引用基类(父类)的地方必须能透明地使用其子类的对象。即子类能够必须能够替换基类能够从出现的地方。子类也可以在基类的基础上新增行为。

原则分析

  1. 讲的是基类和子类的关系,只有这种关系存在时,里氏代换原则才存在。正方形是长方形是理解里氏代换原则的经典例子。
  2. 里氏代换原则可以通俗表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。
  3. 里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

优缺点:

在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:

  1. 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
  2. 提高代码的重用性;
  3. 子类可以形似父类,但又异于父类,"龙生龙,凤生凤,老鼠生来会打洞"是说子拥有父的"种","世界上没有两片完全相同的叶子"是指明子与父的不同;
  4. 提高代码的可扩展性,实现父类的方法就可以"为所欲为"了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;
  5. 提高产品或项目的开放性。

自然界的所有事物都是优点和缺点并存的,即使是鸡蛋,有时候也能挑出骨头来,继承的缺点如下:

  1. 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
  2. 降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;
  3. 增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果----大片的代码需要重构。

经典示例

正方形不是长方形

class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def get_width(self):
        return self.width

    def set_width(self, width):
        self.width = width

    def get_height(self):
        return self.height

    def set_height(self, height):
        self.height = height

    def resize(self):
        if self.get_width() <= self.get_height():
            self.set_width(self.get_width() + 1)


class Square(Rectangle):
    def __init__(self, width=0):
        self.width = width

    def set_width(self, width):
        self.width = width

    def get_width(self):
        return self.width