里氏代换原则 举例 分析

里氏代换原则
定义:里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。
目的:里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
价值:里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
说实话,“任何基类可以出现的地方,子类一定可以出现”,这话太抽象了,我根本找不出另例子来证明,什么是符合这个原则的,什么是不符合这个原则的。
子类一定可以出现,从语言的基本要求角度来讲,这是必须的,面向对象中父类就是可以被子类替换的,那么这个要求针对的是啥?
是需求的层面,因为子类替换父类,在技术上一定是允许的,那么我们在设计上要加些什么样的限制来保证这一点呢。
一个一个的举例吧:
1.类作为函数的参数:函数(类)
这种情况,那对该函数的设计要求是:该函数不能处理个性的需求,也就是说,不应该去处理基类中某一类型所特有的机能,否则这个函数设计不合格。
对类的设计要求是:个性的东西一定要自己处理,不能公开。
2.类做未一个调用函数的对象:类.函数()
这种情况,对该函数的设计要求是,该函数必须处理的是共性的内容,个性的内容要用通过this指针去处理。

通过上面的分析,视乎有点眉目,但仍然感觉不太具体。
一.如下是一个类的设计
// 长方形
class Rectangle{
private:
int h;
int w;
public:
void setH(int value){
h = value;
}
void setW(int vaule){
w = vaule;
}
int getH(){
return h;
}
int getW(){
return w;
}
};
// 正方形
class Square:public Rectangle{
void set(int value){
setH(value);
setW(value);
}
};
二.到这来应该是没有问题的,因为LSP是一个需求级别的设计要求。
现在假设有这么一个要求,保证长方形的宽度一定要大于高度。
函数如下:
class Clinet{
public:
void setRectangle(Rectangle* r){
if(r->getW()getH()){
r->setW(r->getH()+1);
}
}
};
显然这里:设计部符合LSP的设计原则了。
那么问题出在哪里?
怎么办呢?
三.修改设计,其实修改的方法有很多,只要能把问题解决掉就行。
方案1
// 长方形
class Rectangle{
private:
int h;
int w;
public:
void setH(int value){
h = value;
}
void setW(int vaule){
w = vaule;
}
int getH(){
return h;
}
int getW(){
return w;
}
//设计变更
virtual void setRectangle(){
if(getW()<getH()){
setW(getH()+1);
}
}
};
// 正方形
class Square:public Rectangle{
void set(int value){
setH(value);
setW(value);
}
//设计变更
virtual void setRectangle(){

}

};
class Clinet{
public:
void setRectangle(Rectangle* r){
//设计变更
r->setRectangle();
}
};
在好多人眼里,这种修改方案可能不是一个优雅的方案,但很实用,下面在看看更优雅一点的方案。
方案2
// 四边形
class Quadrilateral{
protected:
int h;
int w;
public:
virtual void setH(int value)=0;
virtual void setW(int vaule)=0;
virtual int getH()=0;
virtual int getW()=0;
};
// 长方形
class Rectangle: public Quadrilateral{
public:
virtual void setH(int value){
h = value;
}
virtual void setW(int vaule){
w = vaule;
}
virtual int getH(){
return h;
}
virtual int getW(){
return w;
}
};
// 正方形
class Square:public Quadrilateral{
private:
void setSide(int value){
h = w = value;
}
public:
virtual void setH(int value){
setSide(value);
}
virtual void setW(int vaule){
setSide(vaule);
}
virtual int getH(){
return h;
}
virtual int getW(){
return w;
}
};
class Clinet{
public:
void setRectangle(Rectangle* r){
if(r->getW()getH()){
r->setW(r->getH()+1);
}
}
};

//客户端调用
int main()
{
cout<<“建造者模式演示\n”;

//看代码不用考虑以下内容
int cin_a;
cin>>cin_a;
return 0;

}

总结
设计无非就是解决共性和个性的问题,LSP做为一个原则,其实我们即使不学这个原则,也不一定不遵守这个原则,因为如果你真的做了一个不符合这个原则的设计,那么运行马上就会遇到问题。
解决好共性和个性,就一定是一好的设计,一个符合各种设计原则的设计。
对于LSP这个原则,我其实一直都是理解的云里雾里的,直到现在才把这个东西具体化,所以,我觉得什么抽象的东西都试着举例说明,要不然你永远不会有一个清晰的思路。

猜你喜欢

转载自blog.csdn.net/xie__jin__cheng/article/details/88392902
今日推荐