里氏代换原则
定义:里氏代换原则(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()<r->getH()){
r->setW(r->getH()+1);
}
}
};
显然这里:设计部符合LSP的设计原则了。
那么问题出在哪里?就是Clinet的setRectangle的入参是正方形就出问题了。
怎么办呢?
三.修改设计,其实修改的方法有很多,只要能把问题解决掉就行。
方案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()<r->getH()){
r->setW(r->getH()+1);
}
}
};
//客户端调用
int main()
{
cout<<"建造者模式演示\n";
//看代码不用考虑以下内容
int cin_a;
cin>>cin_a;
return 0;
}
总结
设计无非就是解决共性和个性的问题,LSP做为一个原则,其实我们即使不学这个原则,也不一定不会遵守这个原则,因为如果你真的做了一个不符合这个原则的设计,那么运行马上就会遇到问题。
解决好共性和个性,就一定是一好的设计,一个符合各种设计原则的设计。
对于LSP这个原则,我其实一直都是理解的云里雾里的,直到现在才把这个东西具体化,所以,我觉得什么抽象的东西都试着举例说明,要不然你永远不会有一个清晰的思路。
今天的文章里氏代换原则 举例 分析分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/6056.html