设计模式实例讲解 - 里氏替换
说明
里氏替换的简单理解
子类能够替代父类
我们来举一个例子来说明该原则。
首先,新建一个类 A
class A
{
public function fire() {}
}
类 B
为 A
的子类,并且重写了 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()
}
}
总结一下如何才能不违背里氏替换原则
- 子类抛出的异常必须与父类保持一致
- 子类的前置条件(调用某个方法需满足的条件)不得大于父类的前置条件
- 子类的后置条件(方法返回时必须达到的要求)不得小于父类的后置条件
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: