设计模式实例讲解 - 里氏替换

说明

里氏替换的简单理解

子类能够替代父类

我们来举一个例子来说明该原则。

首先,新建一个类 A

class A
{
    public function fire() {}
}

BA 的子类,并且重写了 fire 方法

class B extends A
{
    public function fire() {}
}

根据里氏替换原则,子类必须能够替代父类。也就是说,虽然子类重写了父类的方法,但是在能够使用父类的场景里面,也一定要能够使用子类。例如,我们的定义一个 doSomething 方法,传入的是类 A

function doSomething(A $obj)
{
    // do something with it.
}

根据里氏替换原则,doSomething 方法同样也应该适用于类 B

反面示例

父类

class VideoPlayer
{
    public function play($file)
    {
        // 播放视频
    }
}

子类

use Exception;
class AviVideoPlayer extends VideoPlayer
{
    public function play($file)
    {
        if (pathinfo($file, PATHINFO_EXTENSION) != 'avi')
        {
            throw new Exception; 
        }
    }
}

子类重写了 play 方法,且抛出了异常,而父类并没有抛出异常。很明显,子类并不能完全替代父类,因此,该例子违反了里氏原则。

面向接口编程

如何才能不违背里氏原则呢?正确的做法就是「面向接口编程」。

首先,用接口定义好方法

interface LessonRepositoryInterface
{
    /**
     * Fetch all records.
     *
     * @return array
     */
    public function getAll();
}

接口作出了约定,子类的实现接口就必须遵从这些约定,一定程度上保证其能遵守里氏原则。

class FileLessonRepository implements LessonRepositoryInterface
{
    public function getAll()
    {
        return [];
    }
}

class DbLessonRepository implements LessonRepositoryInterface
{
    public function getAll()
    {
        return Lesson::all()->toArray()
    }
}

总结一下如何才能不违背里氏替换原则

  1. 子类抛出的异常必须与父类保持一致
  2. 子类的前置条件(调用某个方法需满足的条件)不得大于父类的前置条件
  3. 子类的后置条件(方法返回时必须达到的要求)不得小于父类的后置条件
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1
阿麦

支持一波

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!