什么是里式替換原則
Functions that use pointers of references to base classes must be able to use objects of derived classes without konwing it.
大概意思就是在程序中子類可以替換父類,而且不會產(chǎn)生任何錯誤。但是父類不能替換子類多柑。
符合里式替換原則的代碼是什么樣的呢犬辰?通過一個例子來看一下栗涂。
class Retry {
public async call(fn) {
for(let attemptNumber = 1;; attemptNumber++) {
try{
const response = await fn();
} catch(error) {
throw Error('error');
}
if (response.code < 500) {
return response;
}
}
}
}
class BaseRetry extends Retry{
public async call(fn) {
for(let attemptNumber = 1;; attemptNumber++) {
try{
const response = await fn();
} catch(error) {
throw Error('error');
}
if (response.code < 500) {
response.attemptNumber = attemptNumber;
return response;
}
}
}
}
上面是一個接口重試的例子(當然這里實現(xiàn)非常的簡潔依啰,只是通過它來看一下LSP)奋献,如果返回的code值小于500就返回結(jié)果耕餐,否則繼續(xù)重試凡傅。子類BaseRetry對返回的結(jié)果做了增強,用它來替換new Retry().call()
的運行完全沒有問題肠缔。但如果反過來夏跷,BaseRetry是父類哼转,Retry是子類,Retry替換BaseRetry就可能引起異常槽华,因為子類Retry中取不到attemptNumber壹蔓,子類對父類的返回值做了刪減。
LSP跟多態(tài)有何區(qū)別
首先來看一下什么是多態(tài):
多態(tài)是指子類可以替換父類猫态,在程序運行中調(diào)用子類的方法佣蓉。多態(tài)也是利用繼承來實現(xiàn)。
如果從定義和代碼實現(xiàn)上來看亲雪,多態(tài)和里式替換原則很相似勇凭。但他們關注的角度是不一樣的。從名稱上來看义辕,里式替換是一種設計原則虾标,是來指導繼承關系中子類是如何設計的。而多態(tài)是面向?qū)ο缶幊痰囊淮筇匦怨嘧且环N代碼實現(xiàn)的思路璧函。
遵從里式替換原則的子類在實現(xiàn)時有更多的約束條件,它必須要保證在替換父類的時候不會破壞程序的正確性周崭。還是剛才的例子柳譬,把子類改造一下,它還是能夠利用多態(tài)替換父類续镇,但是卻違反了LSP美澳。
改造后:
class BaseRetry extends Retry{
public async call(fn) {
if(typeof fn !== 'function') {
throw Error('fn must be a function');
}
for(let attemptNumber = 1;; attemptNumber++) {
try{
const response = await fn();
} catch(error) {
throw Error('error');
}
if (response.code < 500) {
return response;
}
}
}
}
改造后的BaseRetry檢驗了參數(shù)類型,不是函數(shù)的話就拋出錯誤摸航,子類替換父類之后有可能報錯制跟。子類的實現(xiàn)違背了父類的約定,所以不符合LSP酱虎。
什么樣的代碼違背了LSP
滿足LSP的子類在設計的時候有哪些要求呢雨膨?
- 子類可以增加自己特有的屬性和方法
- 子類在覆寫父類方法的時候,輸入輸出必須遵從父類的約定读串。輸入相同或者更寬松聊记,輸出相同或者更嚴格。比如子類參數(shù)的類型包含父類參數(shù)的類型恢暖。
那什么樣的代碼明顯違背了LSP排监?
- 子類實現(xiàn)的功能與父類不一樣。
- 子類違背父類對輸入杰捂、輸出舆床、異常的約定。比如父類在輸入為空的時候不做處理,子類在輸入為空的時候拋出異常挨队;父類輸出的時候把整個數(shù)組返回谷暮,子類輸出的時候截取部分返回等。
如果隨著項目的演進盛垦,新實現(xiàn)的子類已經(jīng)不能滿足父類的約定湿弦,那就可以拆成新的類,制定新的約定腾夯。
LSP的意義是什么
里式替換原則可以降低繼承帶來的復雜度省撑。
對于父類中的方法,如果在其子類中有不同的含義俯在,而項目中又較多的使用了多態(tài),那程序出錯的可能性就大大增加娃惯。同時也增加了類管理的難度跷乐。