设计模式之美——里式替换(LSP)
Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it
子类在设计的时候,要遵守父类的行为约定(或者叫协议)。父类定义了函数的行为约定,那子类可以改变函数的内部实现逻辑,但不能改变函数原有的行为约定。这里的行为约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。实际上,定义中父类和子类之间的关系,也可以替换成接口和实现类之间的关系。
例子
和多态区别
如果将上面 SecurityTransporter::sendRequest 改为 appId 不存在时,抛出异常:
那么 SecurityTransporter 不符合里式替换原则,但是还是具有多态性(多态是面向对象程序的语法)。
违背 LSP 场景
1.子类违背父类声明要实现的功能
父类中提供的 sortOrdersByAmount() 订单排序函数,是按照金额从小到大来给订单排序的,而子类重写这个 sortOrdersByAmount() 订单排序函数之后,是按照创建日期来给订单排序的。那子类的设计就违背里式替换原则。
2.子类违背父类对输入、输出、异常的约定
指所有 子类和父类对输入、输出、异常处理不一致的情况
父类出错时返回 null,子类出错时返回异常
父类支持任何类型的整数输入,子类输入负数会抛异常
父类只会抛出一种异常,子类会抛出比父类更多种的异常等等
3.子类违背父类注释中所罗列的任何特殊情况
如何快速识别违背了 LSP 的子类?
那就是拿父类的单元测试去验证子类的代码。如果某些单元测试运行失败,就有可能说明,子类的设计实现没有完全地遵守父类的约定,子类有可能违背了里式替换原则。
版权声明: 本文为 InfoQ 作者【GalaxyCreater】的原创文章。
原文链接:【http://xie.infoq.cn/article/070c2f6a11d77180e1c6d735c】。文章转载请联系作者。
评论