官术网_书友最值得收藏!

2.3 里氏替換原則

2.3.1 里氏替換原則定義

里氏替換原則(Liskov Substitution Principle,LSP)是由麻省理工學院計算機科學系教授芭芭拉·利斯科夫(Barbara Liskov)于 1987 年在“面向對象技術的高峰會議”(OOPSLA)上發表的一篇文章《數據抽象和層次》(Data Abstraction and Hierarchy)里提出的,她提出:繼承必須確保超類所擁有的性質在子類中仍然成立。

1.里氏替換原則

如果S是T的子類型,那么所有T類型的對象都可以在不破壞程序的情況下被S類型的對象替換。

簡單來說,子類可以擴展父類的功能,但不能改變父類原有的功能。也就是說:當子類繼承父類時,除添加新的方法且完成新增功能外,盡量不要重寫父類的方法。這句話包括了四點含義:

·子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法。

·子類可以增加自己特有的方法。

·當子類的方法重載父類的方法時,方法的前置條件(即方法的輸入參數)要比父類的方法更寬松。

·當子類的方法實現父類的方法(重寫、重載或實現抽象方法)時,方法的后置條件(即方法的輸出或返回值)要比父類的方法更嚴格或與父類的方法相等。

2.里氏替換原則的作用

·里氏替換原則是實現開閉原則的重要方式之一。

·解決了繼承中重寫父類造成的可復用性變差的問題。

·是動作正確性的保證,即類的擴展不會給已有的系統引入新的錯誤,降低了代碼出錯的可能性。

·加強程序的健壯性,同時變更時可以做到非常好的兼容性,提高程序的維護性、可擴展性,降低需求變更時引入的風險。

2.3.2 模擬場景

關于里氏替換的場景,最有名的就是“正方形不是長方形”。同時還有一些關于動物的例子,比如鴕鳥、企鵝都是鳥,但是卻不能飛。這樣的例子可以非常形象地幫助我們理解里氏替換中關于兩個類的繼承不能破壞原有特性的含義。

為了從真實的開發場景感受里氏替換原則,這里選擇不同種類的銀行卡作為場景對象進行學習。

我們會使用各種類型的銀行卡,例如儲蓄卡、信用卡,還有一些其他特性的銀行卡。儲蓄卡和信用卡都具備一定的消費功能,但又有一些不同。例如信用卡不宜提現,如果提現可能會產生高額的利息。

下面構建這樣一個模擬場景,假設在構建銀行系統時,儲蓄卡是第一個類,信用卡是第二個類。為了讓信用卡可以使用儲蓄卡的一些方法,選擇由信用卡類繼承儲蓄卡類,討論是否滿足里氏替換原則產生的一些要點。

2.3.3 違背原則方案

儲蓄卡和信用卡在使用功能上類似,都有支付、提現、還款、充值等功能,也有些許不同,例如支付,儲蓄卡做的是賬戶扣款動作,信用卡做的是生成貸款單動作。下面這里模擬先有儲蓄卡的類,之后繼承這個類的基本功能,以實現信用卡的功能。

1.儲蓄卡

在儲蓄卡的功能實現中包括了三個方法:提現、儲蓄、交易流水查詢,這些是模擬儲蓄卡的基本功能。接下來通過繼承儲蓄卡的功能,實現信用卡服務。

2.信用卡

信用卡的功能實現是在繼承了儲蓄卡類后,進行方法重寫:支付withdrawal()、還款recharge()。其實交易流水可以復用,也可以不用重寫這個類。

這種繼承父類方式的優點是復用了父類的核心功能邏輯,但是也破壞了原有的方法。此時繼承父類實現的信用卡類并不滿足里氏替換原則,也就是說,此時的子類不能承擔原父類的功能,直接給儲蓄卡使用。

2.3.4 里氏替換原則改善代碼

儲蓄卡和信用卡在功能使用上有些許類似,在實際的開發過程中也有很多共同可復用的屬性及邏輯。實現這樣的類的最好方式是提取出一個抽象類,由抽象類定義所有卡的共用核心屬性、邏輯,把卡的支付和還款等動作抽象成正向和逆向操作。

1.抽象銀行卡類

在抽象銀行卡類中,提供了基本的卡屬性,包括卡號、開卡時間及三個核心方法。正向入賬,加錢;逆向入賬,減錢。當然,實際的業務開發抽象出來的邏輯會比模擬場景多一些。接下來繼承這個抽象類,實現儲蓄卡的功能邏輯。

2.儲蓄卡類實現

儲蓄卡類中繼承抽象銀行卡父類 BankCard,實現的核心功能包括規則過濾rule、提現withdrawal、儲蓄recharge和新增的擴展方法,即風控校驗 checkRisk。

這樣的實現方式滿足了里氏替換的基本原則,既實現抽象類的抽象方法,又沒有破壞父類中的原有方法。接下來實現信用卡的功能,信用卡的功能可以繼承于儲蓄卡,也可以繼承抽象銀行卡父類。但無論哪種實現方式,都需要遵從里氏替換原則,不可以破壞父類原有的方法。

3.信用卡類實現

信用卡類在繼承父類后,使用了公用的屬性,即卡號 cardNo、開卡時間 cardDate,同時新增了符合信用卡功能的新方法,即貸款loan、還款repayment,并在兩個方法中都使用了抽象類的核心功能。

另外,關于儲蓄卡中的規則校驗方法,新增了自己的規則方法 rule2,并沒有破壞儲蓄卡中的校驗方法。

以上的實現方式都是在遵循里氏替換原則下完成的,子類隨時可以替代儲蓄卡類。

4.功能測試

(1)功能測試:儲蓄卡。

(2)功能測試:信用卡。

(3)功能測試:信用卡替換儲蓄卡。

通過以上的測試結果可以看到,儲蓄卡功能正常,繼承儲蓄卡實現的信用卡功能也正常。同時,原有儲蓄卡類的功能可以由信用卡類支持,即 CashCard creditCard=new CreditCard(...)。

繼承作為面向對象的重要特征,雖然給程序開發帶來了非常大的便利,但也引入了一些弊端。繼承的開發方式會給代碼帶來侵入性,可移植能力降低,類之間的耦合度較高。當對父類修改時,就要考慮一整套子類的實現是否有風險,測試成本較高。

里氏替換原則的目的是使用約定的方式,讓使用繼承后的代碼具備良好的擴展性和兼容性。

在日常開發中使用繼承的地方并不多,在有些公司的代碼規范中也不會允許多層繼承,尤其是一些核心服務的擴展。而繼承多數用在系統架構初期定義好的邏輯上或抽象出的核心功能里。如果使用了繼承,就一定要遵從里氏替換原則,否則會讓代碼出現問題的概率變得更大。

主站蜘蛛池模板: 二手房| 北宁市| 安徽省| 高安市| 桃园市| 都昌县| 潞城市| 南丹县| 交城县| 临朐县| 三穗县| 通榆县| 仙桃市| 镇安县| 凤台县| 台前县| 韶山市| 华阴市| 乌兰县| 米林县| 仁布县| 乌兰县| 自治县| 林芝县| 儋州市| 德兴市| 丹江口市| 广德县| 汶川县| 武宣县| 凤城市| 吴堡县| 昭平县| 正镶白旗| 石家庄市| 禄劝| 顺平县| 子长县| 公主岭市| 尉氏县| 昌吉市|