里氏替换原则
约 1260 字大约 4 分钟
2022-04-22
定义
里氏替换原则(LSP)。
所有引用积累的地方必须能透明地使用其派生类的对象。
简单来说,在代码中任何使用到父类对象的地方,都能够直接使用子类对象直接替代而功能正常。
作用
有一功能 P1,由类 A 完成。现需要将功能 P1 进行扩展,扩展后的功能为 P,其中 P 由原有功能 P1 与新功能 P2 组成。新功能 P 由类 A 的子类 B 来完成,则子类 B 在完成新功能 P2 的同时,有可能会导致原有功能 P1 发生故障。
如果我们遵循里氏替换原则可以保证代码不会发生故障。
里氏替换原则四层含义
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。
子类可以有自己的个性。 在继承父类属性和方法的同时,每个子类也都可以有自己的个性,在父类的基础上扩展自己的功能。前面其实已经提到,当功能扩展时,子类尽量不要重写父类的方法,而是另写一个方法,所以对上面的代码加以更改,使其符合里氏替换原则。
覆盖或实现父类的方法时输入参数可以被放大。 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
覆写或实现父类的方法时输出结果可以被缩小。 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
里氏替换原则优点
保证了父类的复用性。
降低系统出错误的故障,防止误操作。
不会破坏继承的机制。
增强程序的健壮性,版本升级是也可以保持非常好的兼容性。
即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。
里氏替换原则缺点
继承是入侵性的。
只要继承,就必须拥有父类的所有属性与方法。
降低了代码的灵活性。
子类拥有了父类的属性方法,会增多约束。
增强了耦合性。
当父类的常量、变量、方法被修改时,必须考虑子类的修改。
实现方法
如果两个类 A 和 B 之间的关系违反了里氏替换原则(假设 A 为父类),则可以选择以下的重构方案:
- 创建新的抽象类 C 作为 A 和 B 的父类,将 A 和 B 的共同欣慰移动到 C 中。
- 将继承关系改为关联关系。
示例
下面展示其中一种重构方法,将两个类之间的继承关系改为关联关系。
我们考虑运动员和自行车之间的例子,每个运动员都有一辆自行车。
反例
class Bike {
public:
void Move();
void Stop();
void Repair();
protected:
int ChangeColor(int);
private:
int mColor;
};
class Player : private Bike
{
public:
void StartRace();
void EndRace();
protected:
int CurStrength ();
private:
int mMaxStrength;
int mAge;
};
正例
class Bike {
public:
void Move();
void Stop();
void Repair();
protected:
int ChangeColor(int);
private:
int mColor;
};
class Player
{
public:
void StartRace( );
void EndRace( );
protected:
int CurStrength ( );
private:
int mMaxStrength;
int mAge;
Bike * abike;
};
很显然,我们将 Bike
从 Player
的基类改为了其组成的一部分。
总结
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性。如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
里氏替换原则的目的就是增强程序健壮性,版本升级时也可以保持非常好的兼容性。
贡献者
更新日志
79076
-improve(docs): use chinese punctuation于42218
-fix(docs): text typo于1289a
-improve(docs): delete extra whitespace and blank lines于c2111
-modify(docs): remanage folders and rename files于aae8b
-docs: update docs于20068
-整理设计原则整个部分于f5cf9
-设计模式改为设计原则于f86ee
-update于93933
-新增文字+CRLF全部替换为LF于547bb
-升级主题+新增文章+修改格式于