一、概念
里氏替換原則:Liskov Substitution Principle,檢查LSP。子類對象能夠替換程序中父類對象出現的任何地方,並能保證替換完後程序的邏輯行爲不變以及替換前後的邏輯語義和結果正確性不被破壞。
例如:父類邏輯是a+b,子類邏輯是a-b,參數和返回都一樣,但是邏輯變了,這就不符合里氏替換原則。邏輯語義和結果的正確性得到了破壞。
二、案例
如下HealthCheck健康檢查父類,有MysqlHealthCheck子類繼承它,基本語義就是連接到Mysql發SELECT 1;
。
public class HealthCheck{
public Response doCheck(HealthCheckEntity entity) {
// ...
}
}
public class MysqlHealthCheck extends HealthCheck {
private String username;
private String pwd;
public MysqlHealthCheck (HealthCheckEntity entity, String username, String pwd) {
super(entity);
this.username= username;
this.pwd= pwd;
}
@Override
public Response sendRequest(HealthCheckEntity entity) {
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(pwd)) {
entity.setUsername(username);
entity.setPwd(pwd);
}
return super.sendRequest(entity);
}
}
public class Demo {
public void demoFunction(HealthCheck healthCheck) {
HealthCheckEntity entity = new HealthCheckEntity ();
//...省略設置request中數據值的代碼...
Response response = healthCheck.sendRequest(entity);
//...省略其他邏輯...
}
}
// 裏式替換原則
Demo demo = new Demo();
demo.demofunction(new MysqlHealthCheck (/*省略參數*/););
在上面的代碼中,子類MysqlHealthCheck的設計完全符合里氏替換原則,可以無縫替換父類出現的任何位置並且原代碼邏輯行爲不變且正確性也不會得到破壞。但是發現這不就是多態嗎?和多態沒啥區別呀。下面來看和多態的區別。
三、和多態的區別
也稱哪些設計違背了里氏替換原則
- 子類違背父類聲明要實現的功能
最簡單的例子就是父類是按照時間倒敘排序,子類是按照時間升序排序。
- 子類違背父類對出入參以及異常的約定
比如:父類要求運行報錯的時候返回null,沒查到數據但是沒報錯返回
new Object();
,但是子類重寫過後的方法報錯直接返回異常了,得不到父類期待的null值。
- 子類違背父類註釋中所羅列的任何一個特殊說明
比如:父類寫的是參數a不能大於100,而子類重寫後的判斷條件沒有這個100限制,這也算違背里氏替換原則。
以上三點,多態是完全不管的。所以里氏替換是事精,而多態很隨和。
違背里氏替換的案例說明:
假設上面的HealthCheck的子類MysqlHealthCheck的數據庫用戶名密碼沒傳的話就拋出異常,這就違背了里氏替換,因爲父類要求不做任何限制,而你卻強行往裏加。
// 其他類不變
public class MysqlHealthCheck extends HealthCheck {
private String username;
private String pwd;
public MysqlHealthCheck (HealthCheckEntity entity, String username, String pwd) {
super(entity);
this.username= username;
this.pwd= pwd;
}
@Override
public Response sendRequest(HealthCheckEntity entity) {
// 加了限制,違背了里氏替換原則
if (StringUtils.isBlank(username) || StringUtils.isBlank(pwd)) {
throw new RuntimeException();
}
entity.setUsername(username);
entity.setPwd(pwd);
return super.sendRequest(entity);
}
}
四、總結
子類可以新增邏輯或者優化,但是不允許改變父類的語義,比如典型的就是父類要求按照id倒敘排序,你子類不可弄成按照id升序排序。