客戶端不應(yīng)該依賴那些它不需要的接口。
實驗一
考慮一個安全系統(tǒng)。在這個系統(tǒng)中,有一些Door對象,可以被加鎖和解鎖,并且Door對象知道自己是開著還是關(guān)著。這個Door編碼成一個接口,這樣客戶程序就可以使用那些符合Door接口的對象,而不需要依賴于Door的特定實現(xiàn)。
現(xiàn)在,考慮一個這樣的實現(xiàn),TimedDoor,如果門開著的時間過長,它就會發(fā)出警報聲。為了做到這一點(diǎn),TimedDoor對象需要和另一個名為Timer的對象交互。
如果一個對象希望得到超時通知,它可以調(diào)用Timer的Register函數(shù)。該函數(shù)有兩個參數(shù),一個是超時時間,另一個是指向TimerClient對象的引用,其TimeOut函數(shù)會在超時到達(dá)時被調(diào)用。
怎樣將TimerClient類和TimedDoor類聯(lián)系起來,才能在超時時通知到TimedDoor中相應(yīng)的處理代碼呢?比如下面的一種實現(xiàn):
這種做法的問題是,現(xiàn)在Door類依賴于TimerClient了。可是并不是所有種類的Door都需要定時功能。事實上,最初的Door抽象類和定時功能沒有任何關(guān)系。如果創(chuàng)建了無需定時功能的Door的派生類,那么在這些派生類中就必須要提供TimeOut方法的退化實現(xiàn),這就有可能違反LSP。此外,使用這些派生類的應(yīng)用程序即使不使用TimerClient類的定義,也必須要引入它。
這是一個接口污染的例子,Door的接口被一個它不需要的方法污染了。在Door的接口中加入這個方法只是為了能給它的一個子類帶來好處。如果持續(xù)這樣做的話,那么每次子類需要一個新方法時,這個方法就會加到基類中去。這會進(jìn)一步污染基類的接口,使它變“胖”。
此外,每次基類中加入一個方法時,派生類中就必須要實現(xiàn)這個方法(或者定義一個默認(rèn)實現(xiàn))。事實上,有一種特定的相關(guān)實踐,可以使派生類無需實現(xiàn)這些方法,該實踐的做法是把這些接口合并為一個基類,并在這個基類中提供接口中方法的退化實現(xiàn)。但是我們前面已經(jīng)學(xué)過,這種實踐違反了LSP,會帶來維護(hù)和重用方面的問題。
請根據(jù)接口隔離原則,重構(gòu)上面的設(shè)計。
解析(參考):
一個解決方案是創(chuàng)建一個派生自TimerClient的對象,并把對該對象的請求委托給TimedDoor。當(dāng)TimedDoor想要向Timer對象注冊一個超時請求時,它就創(chuàng)建一個DoorTimerAdapter并且把它注冊給Timer。當(dāng)Timer對象發(fā)送TimeOut消息給DoorTimerAdapter時,DoorTimerAdapter把這個消息委托給TimedDoor。這個解決方案遵循ISP原則,并且避免了Door的客戶程序和Timer之間的耦合。即使對代碼清單12-3中所示的Timer進(jìn)行了改變,也不會影響到任何Door的使用者。此外,TimedDoor也不必具有和TimerClient一樣的接口。DoorTimerAdapter會將TimerClient接口轉(zhuǎn)換成TimedDoor接口。因此,這是一個非常通用的解決方案。
實驗二
某軟件公司開發(fā)人員針對 CRM 系統(tǒng)的客戶數(shù)據(jù)顯示模塊設(shè)計了如下圖所示的 CustomerDataDisplay 接口。其中:
方法 readData() 用于從文件中讀取數(shù)據(jù);
方法? transformToXML() 用于將數(shù)據(jù)轉(zhuǎn)換成 XML 格式;
方法 createChart() 用于創(chuàng)建圖表;
方法 displayChart() 用于顯示圖表;
方法 createReport() 用于創(chuàng)建文字報表;
方法 displayReport() 用于顯示文字報表。
在實際使用過程中發(fā)現(xiàn)該接口很不靈活。例如:如果一個具體的數(shù)據(jù)顯示類無須進(jìn)行數(shù)據(jù)轉(zhuǎn)換(源文件本身就是 XML 格式),但由于實現(xiàn)了該接口,將不得不實現(xiàn)其中聲明的 transformToXML() 方法(至少需要提供一個空實現(xiàn));如果需要創(chuàng)建和顯示圖表,除了需要實現(xiàn)與圖表相關(guān)的方法外;還需要實現(xiàn)創(chuàng)建和顯示文字報表的方法。否則程序在編譯時將報錯。
現(xiàn)使用接口隔離原則對其進(jìn)行重構(gòu)。
解析(參考):
在本實例中,由于在接口 CustomerDataDisplay 中定義了太多方法,即該接口承擔(dān)了太多職責(zé),一方面導(dǎo)致該接口的實現(xiàn)類很龐大,在不同的實現(xiàn)類中都不得不實現(xiàn)接口中定義的所有方法,靈活性較差,如果出現(xiàn)大量的空方法,將導(dǎo)致系統(tǒng)中產(chǎn)生大量的無用代碼,影響代碼質(zhì)量。
另一方面由于客戶端針對大接口編程,將在一定程度上破壞程序的封裝性,客戶端看到了不應(yīng)該看到的方法,沒有為客戶端定制接口。因此需要將該接口按照接口隔離原則和單一職責(zé)原則進(jìn)行重構(gòu),將其中的一些方法封裝在不同的小接口中,確保每一個接口使用起來都較為方便,并都承擔(dān)某一單一角色,每個接口中只包含一個客戶端(如模塊或類)所需的方法即可。文章來源:http://www.zghlxwxcb.cn/news/detail-848654.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-848654.html
到了這里,關(guān)于面向?qū)ο笤O(shè)計原則實驗之“接口隔離原則”的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!