如何设计复用性较好的类?
代碼復(fù)用的幾個級別:
- 源代碼級別的復(fù)用
- 模塊級別的復(fù)用(類/抽象類/接口)
- 庫級別的復(fù)用(API)
- 系統(tǒng)級別的復(fù)用:框架
白盒復(fù)用:源代碼可見、可擴展、可修改
黑盒復(fù)用:源代碼不可見,只可調(diào)用API
找源代碼的幾個網(wǎng)站:
grepcode.com
github.com
searchcode.com
本文主要介紹模塊級別的復(fù)用——類/接口
復(fù)用一個類的方式——繼承、委托
繼承
繼承時,子類將繼承父類的所有功能。
子類可以override父類的功能,也可以在父類的基礎(chǔ)上,增加新的功能。
在實現(xiàn)繼承類之前,最好先設(shè)計好繼承層次圖。
子類不可以丟棄父類的屬性或方法,因此在設(shè)計繼承結(jié)構(gòu)時務(wù)必特別小心。
委托(delegation)
委托用于:一個類僅依賴于另一個類的部分功能模塊時,例如,Sorter就將一部分功能委托給了Comparator。
顯式的委托:把被委托類直接傳給委托類
隱式的委托:by the member lookup rules of the language
委托是一種比較低級的共享代碼機制。
庫級別的復(fù)用(API/Package)
庫(Library)是一些具有可重用功能的類的集合。
框架(Framework)是一組可以根據(jù)應(yīng)用需求定制的代碼骨架。
系統(tǒng)級別的復(fù)用(Framework)
框架是一組抽象類、具體類及其鏈接關(guān)系,只有骨架,沒有血肉。
開發(fā)者根據(jù)自己的具體應(yīng)用需求,在框架中填入代碼,形成完整的系統(tǒng)。
白盒框架:通過代碼層面的繼承來進行框架擴展
黑盒框架:通過實現(xiàn)特定的接口來完成代碼復(fù)用
LSP可替換原則
行為子類型
- 子類型可以增加方法,但不能刪除方法。
- 子類型需要實現(xiàn)抽象類型中所有未實現(xiàn)的方法。
- 子類型方法的返回值類型只能與原方法的返回值類型相同或是其子類型。
override一個方法時,設(shè)父類方法返回值類型是T1,子類方法返回值類型是T2,則T2必須與T1相同,或T2是T1的子類型。
原因如下,在實現(xiàn)多態(tài)時,可能有一個父類對象,用子類型實例化,就可能用一個T1類型的變量用于接收父類方法的返回值,這樣在編譯時才能通過。那么在runtime,執(zhí)行的實際上是子類方法,如果子類方法返回的是T1的父類型,則無法被T1類型的變量接收;子類方法如果返回的是T1的子類型,則可以被T1類型的變量接收,這也是一個多態(tài)的過程。因此,要求T2是T1的子類型。 - 子類型方法的參數(shù)必須是與原方法參數(shù)類型相同或是其父類型(這種情況在Java中按overload處理)
override一個方法時,設(shè)父類方法中參數(shù)類型是T1,子類方法參數(shù)類型是T2,則T2必須與T1相同,或是T1的父類型,原因如下。
在實現(xiàn)多態(tài)時,可能有一個父類類型的對象,用子類類型實例化。在調(diào)用這個方法時,傳遞的參數(shù),必須是T1或T1的子類型,這樣編譯才能通過。而在運行時,實際上調(diào)用的是子類的方法,如果子類方法要求的參數(shù)類型T2是T1的子類型,而父類傳入了T1類型,則在運行時,子類方法的參數(shù)無法用T2來接收一個T1類型的對象,就會出錯。因此,要求子類方法接收的參數(shù)類型必須是T1或T1的父類型,這樣才能適配多態(tài)機制。而在Java中,這樣的機制不是override,而是overload。 - 子類型方法拋出的異常必須少于或等于父類方法拋出的異常
這個很容易理解,在調(diào)用父類方法,只處理了一部分異常使得編譯時能通過。而運行時實際上是執(zhí)行的子類方法,如果拋出了額外的異常,就會導(dǎo)致運行時出現(xiàn)未處理的異常。因此子類方法拋出的異常只能變少不能變多。 - 子類型的規(guī)約必須比父類型規(guī)約更強,或相等。
這就意味著子類型擁有更強的不變量、更弱的前置條件、更強的后置條件,因為要求父類接收的輸入,子類必須也能夠接收;子類給出的返回,父類必須也能夠處理。這就要求子類接收的輸入范圍更大、子類給出的輸出范圍更小。
總結(jié)
以上是生活随笔為你收集整理的如何设计复用性较好的类?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 两种重要的图——Snapshot dia
- 下一篇: 字符串格式化漏洞修改GOT表一例