【设计模式系列】OO设计原则之LSP-Liskov替换原则
概要
什么是所谓的替换原则?开发中我们通常会通过继承实现一些子类来达到功能的扩展,比如假设我们有基类B,有个B类型的指针或引用作为某个函数的参数,这时我们创建了子类C继承于B,如果当把指向C类对象的指针作为参数传递时,出现了一些意料之外的异常时,它就违反了LSP。说的有点抽象,到实例再具体说明。
目的
子类类型完全能替换基类类型而不会发生异常
实例与效果
举个LSP经典的例子来说明下吧。比如我们有个Rectangle,可以设定长和宽,同时可以计算它的面积,我们还有正方形的需求,所以我们创建一个Square类,继承于Rectangle。具体实现如下:
class Rectangle { public: virtual void SetWidth(int w) { mWidth = w; } virtual void SetHeigth(int h) { mHeigth = h; } virtual int GetArea() { return mWidth * mHeigth; } protected: int mWidth; int mHeigth; }; class Square extends Rectangle { public: virtual void SetWidth(int w) { mWidth = w; mHeigth = h; } virtual void SetHeigth(int h) { mHeigth = h; mWidth = w; } };
是不是觉得做得挺好,貌似没有什么问题?好,略微分析一下吧。比如我们一般会通过工厂类来获得Rectangle*类型的对象,而在一些条件下用户可能并不知道或者并不关心当前指针是指向什么类型的对象(Rectangle?Square?),这时候问题就来了。假设工厂类方法声明为:
Rectangle* RecFactory::GetInstance();//(static)
调用的代码如下:
Rectangle* rec = RecFactory.GetInstance(); rec.SetWidth(10); rec.SetHeigth(20); int area = rec.GetArea();
当getInstance返回的是指向Square 的对象时,调用方可能觉得结果应该是200,而实际结果却是400。原因很明显,正方形Square的行为特性跟Rectangle不同,导致SetWidth,SetHeigth的行为跟基类的行为特性不一致,最终出现异常的结果。LSP就是这么一回事。