Java設計原則之里氏替換原則

里氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士Barbara Liskov教授和卡內基·梅隆大學Jeannette Wing教授於1994年提出。其嚴格表述以下:若是對每個類型爲S的對象o1,都有類型爲T的對象o2,使得以T定義的全部程序P在全部的對象o1代換o2時,程序P的行爲沒有變化,那麼類型S是類型T的子類型。這個定義比較拗口且難以理解,所以咱們通常使用它的另外一個通俗版定義:編程

里氏代換原則(Liskov Substitution Principle, LSP):全部引用基類(父類)的地方必須能透明地使用其子類的對象。spa

      里氏代換原則告訴咱們,在軟件中將一個基類對象替換成它的子類對象,程序將不會產生任何錯誤和異常,反過來則不成立,若是一個軟件實體使用的是一個子類對象的話,那麼它不必定可以使用基類對象。例如:我喜歡動物,那我必定喜歡狗,由於狗是動物的子類;可是我喜歡狗,不能據此判定我喜歡動物,由於我並不喜歡老鼠,雖然它也是動物。設計

      例若有兩個類,一個類爲BaseClass,另外一個是SubClass類,而且SubClass類是BaseClass類的子類,那麼一個方法若是能夠接受一個BaseClass類型的基類對象base的話,如:method1(base),那麼它必然能夠接受一個BaseClass類型的子類對象sub,method1(sub)可以正常運行。反過來的代換不成立,如一個方法method2接受BaseClass類型的子類對象sub爲參數:method2(sub),那麼通常而言不能夠有method2(base),除非是重載方法。對象

      里氏代換原則是實現開閉原則的重要方式之一,因爲使用基類對象的地方均可以使用子類對象,所以在程序中儘可能使用基類類型來對對象進行定義,而在運行時再肯定其子類類型,用子類對象來替換父類對象。繼承

      在使用里氏代換原則時須要注意以下幾個問題:接口

      (1)子類的全部方法必須在父類中聲明,或子類必須實現父類中聲明的全部方法。根據里氏代換原則,爲了保證系統的擴展性,在程序中一般使用父類來進行定義,若是一個方法只存在子類中,在父類中不提供相應的聲明,則沒法在以父類定義的對象中使用該方法。ip

      (2)  咱們在運用里氏代換原則時,儘可能把父類設計爲抽象類或者接口,讓子類繼承父類或實現父接口,並實如今父類中聲明的方法,運行時,子類實例替換父類實例,咱們能夠很方便地擴展系統的功能,同時無須修改原有子類的代碼,增長新的功能能夠經過增長一個新的子類來實現。里氏代換原則是開閉原則的具體實現手段之一。ci

      (3) Java語言中,在編譯階段,Java編譯器會檢查一個程序是否符合里氏代換原則,這是一個與實現無關的、純語法意義上的檢查,但Java編譯器的檢查是有侷限的。開發

      在Sunny軟件公司開發的CRM系統中,客戶(Customer)能夠分爲VIP客戶(VIPCustomer)和普通客戶(CommonCustomer)兩類,系統須要提供一個發送Email的功能,原始設計方案如圖1所示:編譯器

圖1原始結構圖

      在對系統進行進一步分析後發現,不管是普通客戶仍是VIP客戶,發送郵件的過程都是相同的,也就是說兩個send()方法中的代碼重複,並且在本系統中還將增長新類型的客戶。爲了讓系統具備更好的擴展性,同時減小代碼重複,使用里氏代換原則對其進行重構。

      在本實例中,能夠考慮增長一個新的抽象客戶類Customer,而將CommonCustomer和VIPCustomer類做爲其子類,郵件發送類EmailSender類針對抽象客戶類Customer編程,根據里氏代換原則,可以接受基類對象的地方必然可以接受子類對象,所以將EmailSender中的send()方法的參數類型改成Customer,若是須要增長新類型的客戶,只需將其做爲Customer類的子類便可。重構後的結構如圖2所示:

圖2  重構後的結構圖

      里氏代換原則是實現開閉原則的重要方式之一。在本實例中,在傳遞參數時使用基類對象,除此之外,在定義成員變量、定義局部變量、肯定方法返回類型時均可使用里氏代換原則。針對基類編程,在程序運行時再肯定具體子類。

相關文章
相關標籤/搜索