OOP设计原则之里氏替换原则(LSP)

by:leotse


net_secret

OOP设计原则之依赖倒置原则(DIP)中,我们介绍了OOP设计原则之一的DIP,这里我们将继续介绍另一个设计原则。

我们仍然先来看一个示例,在OOP设计原则之依赖倒置原则(DIP)中,我们使用到了加载Admob广告和Facebook Audience Network广告的例子,我们假定我们会在不同的场景下使用不同的广告源:用户设备上安装了Facebook、Messenger等APP时(Cond 1)请求Facebook
的广告,其他情况(Cond 2)请求Admob的广告。那么我们如何实现呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface IAdManager{
public void displayAd();
}

...

public class FacebookAdManager implements IAdManager{
@Override
public void displayAd(){
...
}
}

...

public class AdmobAdManager implements IAdManager{
@Override
public void displayAd(){
...
}
}

在控制模块中,我们通过不同的Condition判断来调用不同的广告源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AdController{
...
public void display(){
IAdManager adManager;
if (Condition 1){
adManager = new FacebookAdManager();
} else {
adManager = new AdmobAdManager();
}

adManager.displayAd();
}
...
}

在这个示例里,我们在声明的时候用到了基类AdManager,而在程序运行的时候根据设定的条件判断确定真正要实例化的广告源。这里的设计就满足了我们要介绍的里氏替换原则(Liskov Substitution Principle,LSP)。

里氏替换原则要求在任何可以使用基类类型的地方,都可以替换为其子类类型
要遵守这个原则,需要我们在设计的时候将基类设计为抽象类或者接口,同时子类需要实现基类声明的所有方法。在声明的时候使用基类类型即可,而在运行时再确定子类类型。里氏替换原则可以帮助我们提高代码复用程度。

里氏替换原则是基于OOP中的继承和多态。但是如果基类本身是可以实例化的,且子类能够重载基类的方法,那么子类的行为和基类的行为就有可能不一致,这时候不能轻易用子类类型替换基类类型。因此我们在使用LSP的时候需要记住:

尽量不要基于可实例化的父类中继承,而是要基于抽象类和接口继承。