[OOD]违反里氏替换原则的解决方案
关于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;
}
[OOD]违反里氏替换原则的解决方案的更多相关文章
- 【设计模式六大原则2】里氏替换原则(Liskov Substitution Principle)
肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑.其实原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的. 定义1:如果对 ...
- 里氏替换原则(Liskov Substitution Principle,LSP)
肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑.其实原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的. 定义1:如果对每一 ...
- ZT 设计模式六大原则(2):里氏替换原则
设计模式六大原则(2):里氏替换原则 分类: 设计模式 2012-02-22 08:46 23330人阅读 评论(41) 收藏 举报 设计模式class扩展string编程2010 肯定有不少人跟我刚 ...
- 设计模式六大原则(2):里氏替换原则(Liskov Substitution Principle)
肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑.事实上原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的. 定义1:假设对每 ...
- C# 实例解释面向对象编程中的里氏替换原则
在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...
- 【C#设计模式】里氏替换原则
今天,我们再来学习 SOLID 中的"L"对应的原则:里式替换原则. 里氏替换原则 里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能 ...
- 第2章 面向对象的设计原则(SOLID):2_里氏替换原则(LSP)
2. 里氏替换原则(Liskov Substitution Principle,LSP) 2.1 定义 (1)所有使用基类的地方必须能透明地使用子类替换,而程序的行为没有任何变化(不会产生运行结果错误 ...
- 里氏替换原则(Liskov Substitution Principle)
开放封闭原则(Open Closed Principle)是构建可维护性和可重用性代码的基础.它强调设计良好的代码可以不通过修改而扩展,新的功能通过添加新的代码来实现,而不需要更改已有的可工作的代码. ...
- 2.里氏替换原则(Liskov Substitution Principle)
1.定义 里氏替换原则的定义有两种,据说是由麻省理工的一位姓里的女士所提出,因此以其名进行命名. 定义1:如果对一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1所定义的程序P中在o1全都 ...
随机推荐
- web前端--知识点,笔记叠加(javascript,jquery,html5+css3.0,ajax)
函数传参列表,获取方法arguments的使用 function arg(){ var str = '总共传了'+arguments.length+'个参数\n'; for(var i=0;i< ...
- 封装鼠标滚轮事件_mousewheel
function mousewheel(obj,fn){ obj.onmousewheel===null ? obj.onmousewheel=fun : obj.addEventListener(' ...
- Messages.pas里的消息
一.Windows 消息大全 这张表拷贝自万一兄的帖子:http://www.cnblogs.com/del/archive/2008/02/25/1079970.html 但是我希望自己能把这些消息 ...
- Sandcastle是什么
如果你的项目是.net开发,同时需要生成HTML的方法成员文档时,哪么就不得不拿出Sandcastle 因为Sandcastle是微软开发,并开源的文档生成工具; 这种生成进度等待的感觉很爽! 在这里 ...
- python学习第七天
一. subprocess 模块 1. subprocess的介绍:用来替代几个老的模块或是函数,如:os.systam,os.popen,os.spawn*,os.popen2*,co ...
- WPF学习05:2D绘图 使用Transform进行控件变形
在WPF学习04:2D绘图 使用Shape绘基本图形中,我们了解了如何绘制基本的图形. 这一次,我们进一步,研究如何将图形变形. 例子 一个三角形,经Transform形成组合图形: XAML代码: ...
- VC2010 MFC文档类菜单快捷键无法加载问题
问题1. 在菜单中项中输入&Run\tF5, 运行显示Run,而不是Run F5. 问题2.在Accelerator中绑定了快捷键,但运行程序不起作用. 以上2中问题在VC++2008和VC+ ...
- str_replace使用array替换
<?php //替换采集等通过url参数传值 function admin_ff_url_repalce($xmlurl,$order='asc'){ if($order=='asc'){ re ...
- 点击图标 标记为星标记事mac中修改默认的apache网站根目录位置
在Mac OS X中可以很方便的通过开启“Web共享”启用Apache服务:设置方法如下: 打开“系统设置偏好(System Preferences)” -> “共享(Sharing)” -&g ...
- 问题 K: 【USACO2012Feb】植草 {Bronze题2}
按着矩形周长的思路,到当前边的时候,前一层的覆盖数乘以高度加入 ans 就行,然而真正的算法可能并不是这个..只能想到这个了 ; type node=record l,r,mid,sum,delta: ...