設(shè)計(jì)模式六大原則(面向?qū)ο笳Z言)

前言

設(shè)計(jì)模式六大原則網(wǎng)上資料比較多比較亂轰驳,本文將網(wǎng)上的一些好的資料做一下整理厚掷,以便隨時(shí)翻閱。友情提示级解,設(shè)計(jì)模式雖好冒黑,但要謹(jǐn)記切勿過度設(shè)計(jì),否則得不償失勤哗。

一抡爹、單一職責(zé)原則

單一職責(zé)原則(Single Responsibility Principle, SRP):一個(gè)類只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé),或者可以定義為:就一個(gè)類而言芒划,應(yīng)該只有一個(gè)引起它變化的原因冬竟。

  • 單一職責(zé)原則告訴我們:一個(gè)類不能太“累”!在軟件系統(tǒng)中民逼,一個(gè)類(大到模塊泵殴,小到方法)承擔(dān)的職責(zé)越多,它被復(fù)用的可能性就越小拼苍,而且一個(gè)類承擔(dān)的職責(zé)過多笑诅,就相當(dāng)于將這些職責(zé)耦合在一起,當(dāng)其中一個(gè)職責(zé)變化時(shí)疮鲫,可能會影響其他職責(zé)的運(yùn)作吆你,因此要將這些職責(zé)進(jìn)行分離,將不同的職責(zé)封裝在不同的類中俊犯,即將不同的變化原因封裝在不同的類中早处,如果多個(gè)職責(zé)總是同時(shí)發(fā)生改變則可將它們封裝在同一類中。
  • 單一職責(zé)原則是實(shí)現(xiàn)高內(nèi)聚瘫析、低耦合的指導(dǎo)方針,它是最簡單但又最難運(yùn)用的原則默责,需要設(shè)計(jì)人員發(fā)現(xiàn)類的不同職責(zé)并將其分離贬循,而發(fā)現(xiàn)類的多重職責(zé)需要設(shè)計(jì)人員具有較強(qiáng)的分析設(shè)計(jì)能力和相關(guān)實(shí)踐經(jīng)驗(yàn)。
  • 下面通過一個(gè)簡單實(shí)例來進(jìn)一步分析單一職責(zé)原則:

<small>Sunny軟件公司開發(fā)人員針對某CRM(Customer Relationship Management桃序,客戶關(guān)系管理)系統(tǒng)中客戶信息圖形統(tǒng)計(jì)模塊提出了如圖1所示初始設(shè)計(jì)方案:</small>


<small>
getConnection()方法用于連接數(shù)據(jù)庫
findCustomers()用于查詢所有的客戶信息
createChart()用于創(chuàng)建圖表
displayChart()用于顯示圖表
</small>
在上圖中杖虾,CustomerDataChart類承擔(dān)了太多的職責(zé),既包含與數(shù)據(jù)庫相關(guān)的方法媒熊,又包含與圖表生成和顯示相關(guān)的方法奇适。如果在其他類中也需要連接數(shù)據(jù)庫或者使用findCustomers()方法查詢客戶信息坟比,則難以實(shí)現(xiàn)代碼的重用。無論是修改數(shù)據(jù)庫連接方式還是修改圖表顯示方式都需要修改該類嚷往,它不止一個(gè)引起它變化的原因葛账,違背了單一職責(zé)原則。為使其滿足單一職責(zé)原則皮仁,CustomerDataChart可拆分為如下三個(gè)類:

  1. DBUtil:負(fù)責(zé)連接數(shù)據(jù)庫籍琳,包含數(shù)據(jù)庫連接方法getConnection();
  2. CustomerDAO:負(fù)責(zé)操作數(shù)據(jù)庫中的Customer表贷祈,包含對Customer表的增刪改查等方法趋急,如findCustomers();
  3. CustomerDataChart:負(fù)責(zé)圖表的生成和顯示势誊,包含方法createChart()和displayChart()呜达。

使用單一職責(zé)原則對其進(jìn)行重構(gòu)后的圖如下


二、開閉原則

開閉原則(Open-Closed Principle, OCP):一個(gè)軟件實(shí)體應(yīng)當(dāng)對擴(kuò)展開放粟耻,對修改關(guān)閉查近。即軟件實(shí)體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展 。 --Bertrand Meyer

  • 在開閉原則的定義中勋颖,軟件實(shí)體可以指一個(gè)軟件模塊嗦嗡、一個(gè)由多個(gè)類組成的局部結(jié)構(gòu)或一個(gè)獨(dú)立的類
  • 任何軟件都需要面臨一個(gè)很重要的問題饭玲,即它們的需求會隨時(shí)間的推移而發(fā)生變化侥祭。當(dāng)軟件系統(tǒng)需要面對新的需求時(shí),我們應(yīng)該盡量保證系統(tǒng)的設(shè)計(jì)框架是穩(wěn)定的茄厘。如果一個(gè)軟件設(shè)計(jì)符合開閉原則矮冬,那么可以非常方便地對系統(tǒng)進(jìn)行擴(kuò)展,而且在擴(kuò)展時(shí)無須修改現(xiàn)有代碼次哈,使得軟件系統(tǒng)在擁有適應(yīng)性和靈活性的同時(shí)具備較好的穩(wěn)定性和延續(xù)性胎署。隨著軟件規(guī)模越來越大,軟件壽命越來越長窑滞,軟件維護(hù)成本越來越高琼牧,設(shè)計(jì)滿足開閉原則的軟件系統(tǒng)也變得越來越重要。
  • 為了滿足開閉原則哀卫,需要對系統(tǒng)進(jìn)行抽象化設(shè)計(jì)巨坊,抽象化是開閉原則的關(guān)鍵。在Java此改、C#等編程語言中趾撵,可以為系統(tǒng)定義一個(gè)相對穩(wěn)定的抽象層,而將不同的實(shí)現(xiàn)行為移至具體的實(shí)現(xiàn)層中完成共啃。在很多面向?qū)ο缶幊陶Z言中都提供了接口占调、抽象類等機(jī)制暂题,可以通過它們定義系統(tǒng)的抽象層,再通過具體類來進(jìn)行擴(kuò)展究珊。如果需要修改系統(tǒng)的行為薪者,無須對抽象層進(jìn)行任何改動(dòng),只需要增加新的具體類來實(shí)現(xiàn)新的業(yè)務(wù)功能即可苦银,實(shí)現(xiàn)在不修改已有代碼的基礎(chǔ)上擴(kuò)展系統(tǒng)的功能啸胧,達(dá)到開閉原則的要求。

<small>*
Sunny軟件公司開發(fā)的CRM系統(tǒng)可以顯示各種類型的圖表幔虏,如餅狀圖和柱狀圖等纺念,為了支持多種圖表顯示方式,原始設(shè)計(jì)方案如下如所示:
</small>*

在本實(shí)例中想括,由于在ChartDisplay類的display()方法中針對每一個(gè)圖表類編程陷谱,因此增加新的圖表類不得不修改源代碼∩冢可以通過抽象化的方式對系統(tǒng)進(jìn)行重構(gòu)烟逊,使之增加新的圖表類時(shí)無須修改源代碼,滿足開閉原則铺根。具體做法如下:
(1) 增加一個(gè)抽象圖表類AbstractChart宪躯,將各種具體圖表類作為其子類;
(2) ChartDisplay類針對抽象圖表類進(jìn)行編程位迂,由客戶端來決定使用哪種具體圖表访雪。

重構(gòu)后結(jié)構(gòu)圖如下所示

我們引入了抽象圖表類AbstractChart,且ChartDisplay針對抽象圖表類進(jìn)行編程掂林,并通過setChart()方法由客戶端來設(shè)置實(shí)例化的具體圖表對象臣缀,在ChartDisplay的display()方法中調(diào)用chart對象的display()方法顯示圖表。如果需要增加一種新的圖表泻帮,如折線圖LineChart精置,只需要將LineChart也作為AbstractChart的子類,在客戶端向ChartDisplay中注入一個(gè)LineChart對象即可锣杂,無須修改現(xiàn)有類庫的源代碼脂倦。
注意:因?yàn)閤ml和properties等格式的配置文件是純文本文件,可以直接通過VI編輯器或記事本進(jìn)行編輯元莫,且無須編譯赖阻,因此在軟件開發(fā)中,一般不把對配置文件的修改認(rèn)為是對系統(tǒng)源代碼的修改柒竞。如果一個(gè)系統(tǒng)在擴(kuò)展時(shí)只涉及到修改配置文件,而原有的Java代碼或C#代碼沒有做任何修改播聪,該系統(tǒng)即可認(rèn)為是一個(gè)符合開閉原則的系統(tǒng)朽基。


三布隔、里氏替換原則

里氏代換原則(Liskov Substitution Principle)是由麻省理工學(xué)院(MIT)計(jì)算機(jī)科學(xué)實(shí)驗(yàn)室的Liskov女士,在1987年的OOPSLA大會上發(fā)表的一篇文章《Data Abstraction and Hierarchy》里面提出來的稼虎,主要闡述了有關(guān)繼承的一些原則衅檀,也就是什么時(shí)候應(yīng)該使用繼承,什么時(shí)候不應(yīng)該使用繼承霎俩,以及其中的蘊(yùn)涵的原理哀军。2002年,軟件工程大師Robert C. Martin打却,出版了一本《Agile Software Development Principles Patterns and Practices》杉适,在文中他把里氏代換原則最終簡化為一句話:"Subtypes must be substitutable for their base types",也就是說柳击,子類必須能夠替換成它們的基類猿推。

里氏代換原則是實(shí)現(xiàn)開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象捌肴,因此在程序中盡量使用基類類型來對對象進(jìn)行定義蹬叭,而在運(yùn)行時(shí)再確定其子類類型,用子類對象來替換父類對象状知。
在使用里氏代換原則時(shí)需要注意如下幾個(gè)問題:

  1. 子類的所有方法必須在父類中聲明秽五,或子類必須實(shí)現(xiàn)父類中聲明的所有方法。根據(jù)里氏代換原則饥悴,為了保證系統(tǒng)的擴(kuò)展性坦喘,在程序中通常使用父類來進(jìn)行定義,如果一個(gè)方法只存在子類中铺坞,在父類中不提供相應(yīng)的聲明起宽,則無法在以父類定義的對象中使用該方法。
  2. 我們在運(yùn)用里氏代換原則時(shí)济榨,盡量把父類設(shè)計(jì)為抽象類或者接口坯沪,讓子類繼承父類或?qū)崿F(xiàn)父接口,并實(shí)現(xiàn)在父類中聲明的方法擒滑,運(yùn)行時(shí)腐晾,子類實(shí)例替換父類實(shí)例,我們可以很方便地?cái)U(kuò)展系統(tǒng)的功能丐一,同時(shí)無須修改原有子類的代碼藻糖,增加新的功能可以通過增加一個(gè)新的子類來實(shí)現(xiàn)。里氏代換原則是開閉原則的具體實(shí)現(xiàn)手段之一库车。
  3. Java語言中巨柒,在編譯階段,Java編譯器會檢查一個(gè)程序是否符合里氏代換原則,這是一個(gè)與實(shí)現(xiàn)無關(guān)的洋满、純語法意義上的檢查晶乔,但Java編譯器的檢查是有局限的。

<small>在Sunny軟件公司開發(fā)的CRM系統(tǒng)中牺勾,客戶(Customer)可以分為VIP客戶(VIPCustomer)和普通客戶(CommonCustomer)兩類正罢,系統(tǒng)需要提供一個(gè)發(fā)送Email的功能,原始設(shè)計(jì)方案如下圖所示:</small>


在對系統(tǒng)進(jìn)行進(jìn)一步分析后發(fā)現(xiàn)驻民,無論是普通客戶還是VIP客戶翻具,發(fā)送郵件的過程都是相同的,也就是說兩個(gè)send()方法中的代碼重復(fù)回还,而且在本系統(tǒng)中還將增加新類型的客戶裆泳。為了讓系統(tǒng)具有更好的擴(kuò)展性,同時(shí)減少代碼重復(fù)懦趋,使用里氏代換原則對其進(jìn)行重構(gòu)
在本實(shí)例中晾虑,可以考慮增加一個(gè)新的抽象客戶類Customer,而將CommonCustomer和VIPCustomer類作為其子類仅叫,郵件發(fā)送類EmailSender類針對抽象客戶類Customer編程帜篇,根據(jù)里氏代換原則,能夠接受基類對象的地方必然能夠接受子類對象诫咱,因此將EmailSender中的send()方法的參數(shù)類型改為Customer笙隙,如果需要增加新類型的客戶,只需將其作為Customer類的子類即可坎缭。重構(gòu)后的結(jié)構(gòu)如圖2所示:

里氏代換原則是實(shí)現(xiàn)開閉原則的重要方式之一竟痰。在本實(shí)例中,在傳遞參數(shù)時(shí)使用基類對象掏呼,除此以外坏快,在定義成員變量、定義局部變量憎夷、確定方法返回類型時(shí)都可使用里氏代換原則莽鸿。針對基類編程,在程序運(yùn)行時(shí)再確定具體子類拾给。


四祥得、依賴倒置原則

依賴倒置原則(Dependence Inversion Principle)是程序要依賴于抽象接口,不要依賴于具體實(shí)現(xiàn)蒋得。簡單的說就是要求對抽象進(jìn)行編程级及,不要對實(shí)現(xiàn)進(jìn)行編程,這樣就降低了客戶與實(shí)現(xiàn)模塊間的耦合额衙。

依賴倒轉(zhuǎn)原則要求我們在程序代碼中傳遞參數(shù)時(shí)或在關(guān)聯(lián)關(guān)系中饮焦,盡量引用層次高的抽象層類怕吴,即使用接口和抽象類進(jìn)行變量類型聲明、參數(shù)類型聲明县踢、方法返回類型聲明械哟,以及數(shù)據(jù)類型的轉(zhuǎn)換等,而不要用具體類來做這些事情殿雪。為了確保該原則的應(yīng)用,一個(gè)具體類應(yīng)當(dāng)只實(shí)現(xiàn)接口或抽象類中聲明過的方法锋爪,而不要給出多余的方法丙曙,否則將無法調(diào)用到在子類中增加的新方法。

在引入抽象層后其骄,系統(tǒng)將具有很好的靈活性亏镰,在程序中盡量使用抽象層進(jìn)行編程,而將具體類寫在配置文件中拯爽,這樣一來索抓,如果系統(tǒng)行為發(fā)生變化,只需要對抽象層進(jìn)行擴(kuò)展毯炮,并修改配置文件逼肯,而無須修改原有系統(tǒng)的源代碼,在不修改的情況下來擴(kuò)展系統(tǒng)的功能桃煎,滿足開閉原則的要求篮幢。

在實(shí)現(xiàn)依賴倒轉(zhuǎn)原則時(shí),我們需要針對抽象層編程为迈,而將具體類的對象通過依賴注入(DependencyInjection, DI)的方式注入到其他對象中三椿,依賴注入是指當(dāng)一個(gè)對象要與其他對象發(fā)生依賴關(guān)系時(shí),通過抽象來注入所依賴的對象葫辐。常用的注入方式有三種搜锰,分別是:構(gòu)造注入,設(shè)值注入(Setter注入)和接口注入耿战。構(gòu)造注入是指通過構(gòu)造函數(shù)來傳入具體類的對象蛋叼,設(shè)值注入是指通過Setter方法來傳入具體類的對象,而接口注入是指通過在接口中聲明的業(yè)務(wù)方法來傳入具體類的對象昆箕。這些方法在定義時(shí)使用的是抽象類型鸦列,在運(yùn)行時(shí)再傳入具體類型的對象,由子類對象來覆蓋父類對象鹏倘。

<small>Sunny軟件公司開發(fā)人員在開發(fā)某CRM系統(tǒng)時(shí)發(fā)現(xiàn):該系統(tǒng)經(jīng)常需要將存儲在TXT或Excel文件中的客戶信息轉(zhuǎn)存到數(shù)據(jù)庫中薯嗤,因此需要進(jìn)行數(shù)據(jù)格式轉(zhuǎn)換。在客戶數(shù)據(jù)操作類中將調(diào)用數(shù)據(jù)格式轉(zhuǎn)換類的方法實(shí)現(xiàn)格式轉(zhuǎn)換和數(shù)據(jù)庫插入操作纤泵,初始設(shè)計(jì)方案結(jié)構(gòu)如下圖所示:</small>

在編碼實(shí)現(xiàn)圖1所示結(jié)構(gòu)時(shí)骆姐,Sunny軟件公司開發(fā)人員發(fā)現(xiàn)該設(shè)計(jì)方案存在一個(gè)非常嚴(yán)重的問題镜粤,由于每次轉(zhuǎn)換數(shù)據(jù)時(shí)數(shù)據(jù)來源不一定相同,因此需要更換數(shù)據(jù)轉(zhuǎn)換類玻褪,如有時(shí)候需要將TXTDataConvertor改為ExcelDataConvertor肉渴,此時(shí),需要修改CustomerDAO的源代碼带射,而且在引入并使用新的數(shù)據(jù)轉(zhuǎn)換類時(shí)也不得不修改CustomerDAO的源代碼同规,系統(tǒng)擴(kuò)展性較差,違反了開閉原則窟社,現(xiàn)需要對該方案進(jìn)行重構(gòu)券勺。

在本實(shí)例中,由于CustomerDAO針對具體數(shù)據(jù)轉(zhuǎn)換類編程灿里,因此在增加新的數(shù)據(jù)轉(zhuǎn)換類或者更換數(shù)據(jù)轉(zhuǎn)換類時(shí)都不得不修改CustomerDAO的源代碼关炼。我們可以通過引入抽象數(shù)據(jù)轉(zhuǎn)換類解決該問題,在引入抽象數(shù)據(jù)轉(zhuǎn)換類DataConvertor之后匣吊,CustomerDAO針對抽象類DataConvertor編程儒拂,而將具體數(shù)據(jù)轉(zhuǎn)換類名存儲在配置文件中,符合依賴倒轉(zhuǎn)原則色鸳。根據(jù)里氏代換原則社痛,程序運(yùn)行時(shí),具體數(shù)據(jù)轉(zhuǎn)換類對象將替換DataConvertor類型的對象命雀,程序不會出現(xiàn)任何問題褥影。更換具體數(shù)據(jù)轉(zhuǎn)換類時(shí)無須修改源代碼,只需要修改配置文件咏雌;如果需要增加新的具體數(shù)據(jù)轉(zhuǎn)換類凡怎,只要將新增數(shù)據(jù)轉(zhuǎn)換類作為DataConvertor的子類并修改配置文件即可,原有代碼無須做任何修改赊抖,滿足開閉原則统倒。重構(gòu)后的結(jié)構(gòu)如下圖所示:


在上述重構(gòu)過程中,我們使用了開閉原則氛雪、里氏代換原則和依賴倒轉(zhuǎn)原則房匆,在大多數(shù)情況下,這三個(gè)設(shè)計(jì)原則會同時(shí)出現(xiàn)报亩,開閉原則是目標(biāo)浴鸿,里氏代換原則是基礎(chǔ),依賴倒轉(zhuǎn)原則是手段弦追,它們相輔相成岳链,相互補(bǔ)充,目標(biāo)一致劲件,只是分析問題時(shí)所站角度不同而已掸哑。

Spring 中所謂的控制反轉(zhuǎn)(IOC)依賴注入(DI)其實(shí)就是依賴倒置原則的體現(xiàn)约急。


五、接口隔離原則

接口隔離原則(Interface Segregation Principle, ISP):使用多個(gè)專門的接口苗分,而不使用單一的總接口厌蔽,即客戶端不應(yīng)該依賴那些它不需要的接口。

根據(jù)接口隔離原則摔癣,當(dāng)一個(gè)接口太大時(shí)奴饮,我們需要將它分割成一些更細(xì)小的接口,使用該接口的客戶端僅需知道與之相關(guān)的方法即可择浊。每一個(gè)接口應(yīng)該承擔(dān)一種相對獨(dú)立的角色拐云,不干不該干的事,該干的事都要干近她。這里的“接口”往往有兩種不同的含義:一種是指一個(gè)類型所具有的方法特征的集合,僅僅是一種邏輯上的抽象膳帕;另外一種是指某種語言具體的“接口”定義粘捎,有嚴(yán)格的定義和結(jié)構(gòu),比如Java語言中的interface危彩。對于這兩種不同的含義攒磨,ISP的表達(dá)方式以及含義都有所不同:

  1. 當(dāng)把“接口”理解成一個(gè)類型所提供的所有方法特征的集合的時(shí)候,這就是一種邏輯上的概念汤徽,接口的劃分將直接帶來類型的劃分娩缰。可以把接口理解成角色谒府,一個(gè)接口只能代表一個(gè)角色拼坎,每個(gè)角色都有它特定的一個(gè)接口,此時(shí)完疫,這個(gè)原則可以叫做“角色隔離原則”泰鸡。
  2. 如果把“接口”理解成狹義的特定語言的接口,那么ISP表達(dá)的意思是指接口僅僅提供客戶端需要的行為壳鹤,客戶端不需要的行為則隱藏起來盛龄,應(yīng)當(dāng)為客戶端提供盡可能小的單獨(dú)的接口,而不要提供大的總接口芳誓。在面向?qū)ο缶幊陶Z言中余舶,實(shí)現(xiàn)一個(gè)接口就需要實(shí)現(xiàn)該接口中定義的所有方法,因此大的總接口使用起來不一定很方便锹淌,為了使接口的職責(zé)單一匿值,需要將大接口中的方法根據(jù)其職責(zé)不同分別放在不同的小接口中,以確保每個(gè)接口使用起來都較為方便赂摆,并都承擔(dān)某一單一角色千扔。接口應(yīng)該盡量細(xì)化憎妙,同時(shí)接口中的方法應(yīng)該盡量少,每個(gè)接口中只包含一個(gè)客戶端(如子模塊或業(yè)務(wù)邏輯類)所需的方法即可曲楚,這種機(jī)制也稱為“定制服務(wù)”厘唾,即為不同的客戶端提供寬窄不同的接口。

<small>Sunny軟件公司開發(fā)人員針對某CRM系統(tǒng)的客戶數(shù)據(jù)顯示模塊設(shè)計(jì)了如圖1所示接口龙誊,其中方法dataRead()用于從文件中讀取數(shù)據(jù)抚垃,方法transformToXML()用于將數(shù)據(jù)轉(zhuǎn)換成XML格式,方法createChart()用于創(chuàng)建圖表趟大,方法displayChart()用于顯示圖表鹤树,方法createReport()用于創(chuàng)建文字報(bào)表,方法displayReport()用于顯示文字報(bào)表逊朽,初始設(shè)計(jì)方案結(jié)構(gòu)如下圖所示</small>


在實(shí)際使用過程中發(fā)現(xiàn)該接口很不靈活罕伯,例如如果一個(gè)具體的數(shù)據(jù)顯示類無須進(jìn)行數(shù)據(jù)轉(zhuǎn)換(源文件本身就是XML格式),但由于實(shí)現(xiàn)了該接口叽讳,將不得不實(shí)現(xiàn)其中聲明的transformToXML()方法(至少需要提供一個(gè)空實(shí)現(xiàn))追他;如果需要?jiǎng)?chuàng)建和顯示圖表,除了需實(shí)現(xiàn)與圖表相關(guān)的方法外岛蚤,還需要實(shí)現(xiàn)創(chuàng)建和顯示文字報(bào)表的方法邑狸,否則程序編譯時(shí)將報(bào)錯(cuò)。

 由于在接口CustomerDataDisplay中定義了太多方法涤妒,即該接口承擔(dān)了太多職責(zé)单雾,一方面導(dǎo)致該接口的實(shí)現(xiàn)類很龐大,在不同的實(shí)現(xiàn)類中都不得不實(shí)現(xiàn)接口中定義的所有方法她紫,靈活性較差戚绕,如果出現(xiàn)大量的空方法粤剧,將導(dǎo)致系統(tǒng)中產(chǎn)生大量的無用代碼店展,影響代碼質(zhì)量聂儒;另一方面由于客戶端針對大接口編程,將在一定程序上破壞程序的封裝性围详,客戶端看到了不應(yīng)該看到的方法朴乖,沒有為客戶端定制接口。因此需要將該接口按照接口隔離原則和單一職責(zé)原則進(jìn)行重構(gòu)助赞,將其中的一些方法封裝在不同的小接口中买羞,確保每一個(gè)接口使用起來都較為方便,并都承擔(dān)某一單一角色雹食,每個(gè)接口中只包含一個(gè)客戶端(如模塊或類)所需的方法即可畜普。
  通過使用接口隔離原則,本實(shí)例重構(gòu)后的結(jié)構(gòu)如下圖所示:

在使用接口隔離原則時(shí)群叶,我們需要注意控制接口的粒度吃挑,接口不能太小钝荡,如果太小會導(dǎo)致系統(tǒng)中接口泛濫,不利于維護(hù)舶衬;接口也不能太大埠通,太大的接口將違背接口隔離原則,靈活性較差逛犹,使用起來很不方便端辱。一般而言,接口中僅包含為某一類用戶定制的方法即可虽画,不應(yīng)該強(qiáng)迫客戶依賴于那些它們不用的方法舞蔽。


六、迪米特法則

迪米特法則(Law of Demeter)又叫作最少知識原則(Least Knowledge Principle 簡寫LKP)码撰,就是說一個(gè)對象應(yīng)當(dāng)對其他對象有盡可能少的了解,不和陌生人說話渗柿。英文簡寫為: LoD.

如果一個(gè)系統(tǒng)符合迪米特法則,那么當(dāng)其中某一個(gè)模塊發(fā)生修改時(shí)脖岛,就會盡量少地影響其他模塊朵栖,擴(kuò)展會相對容易,這是對軟件實(shí)體之間通信的限制鸡岗,迪米特法則要求限制軟件實(shí)體之間通信的寬度和深度。迪米特法則可降低系統(tǒng)的耦合度编兄,使類與類之間保持松散的耦合關(guān)系轩性。
迪米特法則還有幾種定義形式,包括:不要和“陌生人”說話狠鸳、只與你的直接朋友通信等揣苏,在迪米特法則中,對于一個(gè)對象件舵,其朋友包括以下幾類:

  1. 當(dāng)前對象本身(this)卸察;
  2. 以參數(shù)形式傳入到當(dāng)前對象方法中的對象;
  3. 當(dāng)前對象的成員對象铅祸;
  4. 如果當(dāng)前對象的成員對象是一個(gè)集合坑质,那么集合中的元素也都是朋友;
  5. 當(dāng)前對象所創(chuàng)建的對象临梗。

任何一個(gè)對象涡扼,如果滿足上面的條件之一,就是當(dāng)前對象的“朋友”盟庞,否則就是“陌生人”吃沪。在應(yīng)用迪米特法則時(shí),一個(gè)對象只能與直接朋友發(fā)生交互什猖,不要與“陌生人”發(fā)生直接交互票彪,這樣做可以降低系統(tǒng)的耦合度红淡,一個(gè)對象的改變不會給太多其他對象帶來影響。
迪米特法則要求我們在設(shè)計(jì)系統(tǒng)時(shí)降铸,應(yīng)該盡量減少對象之間的交互在旱,如果兩個(gè)對象之間不必彼此直接通信,那么這兩個(gè)對象就不應(yīng)當(dāng)發(fā)生任何直接的相互作用垮耳,如果其中的一個(gè)對象需要調(diào)用另一個(gè)對象的某一個(gè)方法的話颈渊,可以通過第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用。簡言之终佛,就是通過引入一個(gè)合理的第三者來降低現(xiàn)有對象之間的耦合度俊嗽。

在將迪米特法則運(yùn)用到系統(tǒng)設(shè)計(jì)中時(shí),要注意下面的幾點(diǎn):

  1. 在類的劃分上铃彰,應(yīng)當(dāng)盡量創(chuàng)建松耦合的類绍豁,類之間的耦合度越低,就越有利于復(fù)用牙捉,一個(gè)處在松耦合中的類一旦被修改竹揍,不會對關(guān)聯(lián)的類造成太大波及。
  2. 在類的結(jié)構(gòu)設(shè)計(jì)上邪铲,每一個(gè)類都應(yīng)當(dāng)盡量降低其成員變量和成員函數(shù)的外部訪問權(quán)限(public>default>protected>private)芬位。
  3. 在類的設(shè)計(jì)上,只要有可能带到,一個(gè)類型應(yīng)當(dāng)設(shè)計(jì)成不變類昧碉。
  4. 在對其他類的引用上,一個(gè)對象對其他對象的引用應(yīng)當(dāng)降到最低揽惹。

<small>Sunny軟件公司所開發(fā)CRM系統(tǒng)包含很多業(yè)務(wù)操作窗口被饿,在這些窗口中,某些界面控件之間存在復(fù)雜的交互關(guān)系搪搏,一個(gè)控件事件的觸發(fā)將導(dǎo)致多個(gè)其他界面控件產(chǎn)生響應(yīng)狭握,例如,當(dāng)一個(gè)按鈕(Button)被單擊時(shí)疯溺,對應(yīng)的列表框(List)、組合框(ComboBox)囱嫩、文本框(TextBox)嗅辣、文本標(biāo)簽(Label)等都將發(fā)生改變挠说,在初始設(shè)計(jì)方案中,界面控件之間的交互關(guān)系可簡化為如下圖所示結(jié)構(gòu)。</small>

由于界面控件之間的交互關(guān)系復(fù)雜,導(dǎo)致在該窗口中增加新的界面控件時(shí)需要修改與之交互的其他控件的源代碼缸兔,系統(tǒng)擴(kuò)展性較差吹艇,也不便于增加和刪除新控件, 現(xiàn)使用迪米特對其進(jìn)行重構(gòu)惰蜜。 在本實(shí)例中,可以通過引入一個(gè)專門用于控制界面控件交互的中間類(Mediator)來降低界面控件之間的耦合度受神。引入中間類之后抛猖,界面控件之間不再發(fā)生直接引用,而是將請求先轉(zhuǎn)發(fā)給中間類鼻听,再由中間類來完成對其他控件的調(diào)用财著。當(dāng)需要增加或刪除新的控件時(shí),只需修改中間類即可撑碴,無須修改新增控件或已有控件的源代碼撑教,重構(gòu)后結(jié)構(gòu)如下圖所示:

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市醉拓,隨后出現(xiàn)的幾起案子伟姐,更是在濱河造成了極大的恐慌,老刑警劉巖廉嚼,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玫镐,死亡現(xiàn)場離奇詭異倒戏,居然都是意外死亡怠噪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門杜跷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來傍念,“玉大人,你說我怎么就攤上這事葛闷”锘保” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵淑趾,是天一觀的道長阳仔。 經(jīng)常有香客問我,道長扣泊,這世上最難降的妖魔是什么近范? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任嘶摊,我火速辦了婚禮,結(jié)果婚禮上评矩,老公的妹妹穿的比我還像新娘叶堆。我一直安慰自己,他們只是感情好斥杜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布虱颗。 她就那樣靜靜地躺著,像睡著了一般蔗喂。 火紅的嫁衣襯著肌膚如雪忘渔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天弱恒,我揣著相機(jī)與錄音辨萍,去河邊找鬼。 笑死返弹,一個(gè)胖子當(dāng)著我的面吹牛锈玉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播义起,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拉背,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了默终?” 一聲冷哼從身側(cè)響起椅棺,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎齐蔽,沒想到半個(gè)月后两疚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡含滴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年诱渤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谈况。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勺美,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碑韵,到底是詐尸還是另有隱情赡茸,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布祝闻,位于F島的核電站占卧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜华蜒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一舷蒲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧友多,春花似錦牲平、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至启绰,卻和暖如春昂儒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背委可。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工渊跋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人着倾。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓拾酝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卡者。 傳聞我的和親對象是個(gè)殘疾皇子蒿囤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計(jì)模式之六大原則(轉(zhuǎn)載) 關(guān)于設(shè)計(jì)模式的六大設(shè)計(jì)原則的資料網(wǎng)上很多...
    霄霄霄霄閱讀 899評論 0 1
  • 單一職責(zé)原則 (SRP) 全稱 SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,765評論 2 5
  • 設(shè)計(jì)模式之六大原則(轉(zhuǎn)載) 設(shè)計(jì)模式之六大原則(轉(zhuǎn)載) 關(guān)于設(shè)計(jì)模式的六大設(shè)計(jì)原則的資料網(wǎng)上很多,但是很多地方解釋...
    天涯人1196閱讀 330評論 0 0
  • 本文出自《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》中的第一章崇决。 1材诽、優(yōu)化代碼的第一步——單一職責(zé)原則 單一職責(zé)原則的...
    MrSimp1e0閱讀 1,770評論 1 13
  • D7 早上從德令哈出發(fā)直接啟程前往茶卡鹽湖,又是一路的雨恒傻,對茶卡鹽湖的游...
    小門墩閱讀 214評論 0 1