关于OOD中的里氏替换原则,大家耳熟能祥了,不再展开,可以参考设计模式的六大设计原则之里氏替换原则。这里尝试讨论常常违反的两种形式和解决方案。

违反里氏替换原则的根源是对子类及父类关系不明确。我们在设计继承关系常常受一些主观认识的左右,比如Robert C. Martin提到的线段与线的关系,以及被大家说到烂的正方形与矩形。从以前的经验我们认为它们符合继承关系,比如线段是线的较短形式,正方形是矩形的一个特例。但事实上它们并不能完全的包容和替代。

以集合的形式表示,左图是里氏替换的目标,子类可以完全包容了父类的特性集合。右图则是说两者存在不兼容的特性集合: 

对应的解决方案就是进一步抽象,将它们之前的关系从语言的角度重新定义,也许果真是is-a, 也许是has-a,也许它们只是兄弟。 
基本的思路如下:

1. 找到更高层次的抽象

 
以Robert C. Martin举的线与线段为例, 初始实现Line是LineSegment的基类:

// Line代表经过两点(P1,P2)的一条的直线
class Line{
public:
double GetSlope() const;
Point GetP1() const;
Point GetP2() const;
virtual bool IsOn(const Point&) const; private:
Point itsP1;
Point itsP2;
} // LineSegment则是由两点(P1,P2)连接的线段。
class LineSegment : public Line {
public:
virtual bool IsOn(const Point&) const;
}

其中IsOn函数用于计算某个点在不在直线或线段上。对于直线而言,一个点在不在其上仅仅取决于这个点相对于直线的两个点的关系。而对于线段而言,还是它是否在线程起止边界内。两者对于这个接口函数的判断条件并不相同,所以LineSegment无法直接代替父类,违反了里氏替换原则。

解决方案是将这个不一致的接口排除掉,剩下的公共接口做为直线和线段的基类,即定义一个LinearObject做为Line及LineSegment的基类:

class Line{
public:
double GetSlope() const;
Point GetP1() const;
Point GetP2() const;
// 纯虚函数的意义在于,确保使用基类的客户代码不会使用这个接口函数
virtual bool IsOn(const Point&) const = ; private:
Point itsP1;
Point itsP2;
}

2. 改为has-a关系

另一种解决方案,是针对继承关系太过牵强的情况,比如所谓的is-implemented-in-terms-of (由谁实现)的情况,不如转化为组合模式,如下面的关系: 
 
Scott Meyers在Effective C++ 3e, Item 38提到一个案例。比如准备基于std::list实现一个Set。初步想法是期望保持与list相同的接口,于是定义为:

template<typename T>
class Set : public std::List<T> {...}

但Set与List在行为有一个巨大的差异是Set不允许重复的元素,所以也违反了里氏替换原则。 
解决方案就是,使用std::list实现,就是一个has-a关系,可以定义为:

template<typename T>
class Set {
public:
void insert(const T& item);
void remove(const T& item);
... private:
std::list<T> rep;
}

最新文章

  1. c++ 注册类到 lua
  2. makefile 函数集
  3. MyEclipse 2015 CI
  4. PL/SQL学习(四)存储过程和函数
  5. Debian 8.0(Jessie) 无线网卡,ATI显卡驱动和输入法等安装记录。
  6. JAVA I/O流 之入门
  7. 【三】注入框架RoboGuice使用:(Your First Resource Injection)
  8. Hibernate 注解说明
  9. Mac上安装openCV(Java版本)
  10. maven导入多模块项目
  11. 【前端单元测试入门05】react的单元测试之jest
  12. 【python系列】--Python变量和数据类型
  13. 翻译:SELECT INTO语句(已提交到MariaDB官方手册)
  14. 根据word模板dotx文件创建word
  15. 第一章 Python入门
  16. Mysql 性能优化4 mysql参数配置
  17. 【Java】快速排序的非递归实现
  18. Mina.Net实现的断线重连
  19. 获取微信小程序源码
  20. IrisBlur - 虹膜模糊

热门文章

  1. centOS 一键php环境安装-php博弈
  2. HTML:form表单总结,input,select,option,textarea,label
  3. android 线程
  4. (转)可收缩、扩展的TextView
  5. 每日一“酷”之difflib
  6. WPF学习05:2D绘图 使用Transform进行控件变形
  7. Python中的除法
  8. VB数据库经典实例总结(二)
  9. 【原创】书本翻页效果booklet jquery插件系列之简介
  10. SQL Server2008附加数据库之后显示为只读