第一篇:普通程序員到高級IT工程師必經(jīng)之路----了解“高內(nèi)聚,低耦合,代碼復(fù)用”
------------------我個(gè)人對編程開發(fā)的總結(jié) 面向?qū)ο箝_發(fā),一切皆對象
方法函數(shù)應(yīng)該提煉出來,代碼服用 使用工廠模式便于管理維護(hù)
面向接口開發(fā) 方法不要隨便public 分層開發(fā)
公用的方法 提煉出來 業(yè)務(wù)邏輯和顯示應(yīng)該分離
-------------------下面是一篇網(wǎng)上的好文章
首先要知道一個(gè)軟件是由多個(gè)子程序組裝而成,而一個(gè)程序由多個(gè)模塊(方法)構(gòu)成!
而內(nèi)聚就是指程序內(nèi)的各個(gè)模塊之間的關(guān)系緊密程度,偶合就是各個(gè)外部程序(子程序)之間的關(guān)系緊密程度.所以很易明白,為什么要高內(nèi)聚?模塊之間的關(guān)系越緊密,出錯(cuò)就越少!低偶合?子程序間的關(guān)系越復(fù)雜,就會產(chǎn)生更多的意想不到的錯(cuò)誤!會給以后的維護(hù)工作帶來很多麻煩!
一個(gè)優(yōu)秀軟件開發(fā)人員的必修課:高內(nèi)聚
高內(nèi)聚 Java 軟件工程軟件模式
一個(gè)重要的模式:高內(nèi)聚。
2.高內(nèi)聚(High Cohesion)
高 內(nèi)聚是另一個(gè)普遍用來評判軟件設(shè)計(jì)質(zhì)量的標(biāo)準(zhǔn)。內(nèi)聚,更為專業(yè)的說法叫功能內(nèi)聚,是對軟件系統(tǒng)中元素職責(zé)相關(guān)性和集中度的度量。如果元素具有高度相關(guān)的職 責(zé),除了這些職責(zé)內(nèi)的任務(wù),沒有其它過多的工作,那么該元素就具有高內(nèi)聚性,反之則為低內(nèi)聚性。高內(nèi)聚要求軟件系統(tǒng)中的各個(gè)元素具有較高的協(xié)作性,因?yàn)樵?我們在完成軟件需求中的一個(gè)功能,可能需要做各種事情,但是具有高內(nèi)聚性的一個(gè)元素,只完成它職責(zé)內(nèi)的事情,而把那些不在它職責(zé)內(nèi)的事情拿去請求別人來完 成。這就好像,如果我是一個(gè)項(xiàng)目經(jīng)理,我的職責(zé)是監(jiān)控和協(xié)調(diào)我的項(xiàng)目各個(gè)階段的工作。當(dāng)我的項(xiàng)目進(jìn)入需求分析階段,我會請求需求分析員來完成;當(dāng)我的項(xiàng)目 進(jìn)入開發(fā)階段,我會請求軟件開發(fā)人員來完成;當(dāng)我的項(xiàng)目需要測試的時(shí)候,我會請求測試人員。。。如果我參與了開發(fā),我就不是一個(gè)高內(nèi)聚的元素,因?yàn)?開發(fā)不是我的職責(zé)。我們的項(xiàng)目為什么要高內(nèi)聚呢?我覺得可以從可讀性、復(fù)用性、可維護(hù)性和易變更性四個(gè)方面來理解。
1.可讀性
一 個(gè)人寫文章、講事情,條理清晰才能易于理解,這同樣發(fā)生在讀寫軟件代碼上。如果一堆代碼寫得一團(tuán)亂麻,東一個(gè)跳轉(zhuǎn)西一個(gè)調(diào)用,讀它的人會感覺非常頭疼。這 種事情也許一直在寫程序的你我都曾經(jīng)有過經(jīng)歷。如果一段程序條理非常清晰,每個(gè)類通過名稱或說明都能清楚明白它的意義,類的每個(gè)屬性、函數(shù)也都是易于理解 的它所應(yīng)當(dāng)完成的任務(wù)和行為,這段程序的可讀性必然提高。在軟件產(chǎn)業(yè)越來越密集,軟件產(chǎn)業(yè)中開發(fā)人員協(xié)作越來越緊密、分工越來越細(xì)的今天,軟件可讀性的要 求相信也越來越為人們所重視。
2.復(fù)用性
在軟件開發(fā)中,最低等級的復(fù)用是代碼拷貝,然后是函數(shù)的復(fù)用、對象的復(fù)用、組件 的復(fù)用。軟件開發(fā)中最懶的人是最聰明的人,他們總是想到復(fù)用。在代碼編寫的時(shí)候突然發(fā)現(xiàn)某個(gè)功能是曾經(jīng)實(shí)現(xiàn)過的功能,直接把它拷貝過來就ok了。如果這段 代碼在同一個(gè)對象中,那么就提出來寫一個(gè)函數(shù)到處調(diào)用就行了。如果不是在同一個(gè)對象中呢,就將其抽象成一個(gè)對象到處調(diào)用吧。如果不在一個(gè)項(xiàng)目中呢,那就做 成組件給各個(gè)項(xiàng)目引用吧。代碼復(fù)用也使我們的代碼在復(fù)用的過程中不斷精化、不斷健壯、提高代碼質(zhì)量。代碼的復(fù)用的確給我們的開發(fā)帶來了不少便利,但是一段 代碼能否在各個(gè)需要的地方都能復(fù)用呢?這給我們的軟件開發(fā)質(zhì)量提出了新的要求:好的代碼可以復(fù)用,不好的則不行。軟件中的一個(gè)對象如果能保證能完成自己職 能范圍內(nèi)的各項(xiàng)任務(wù),同時(shí)又不去理會與自己職能無關(guān)的其它任務(wù),那么它就能夠保證功能的相對獨(dú)立性,也就可以脫離自己所處的環(huán)境而復(fù)用到其它環(huán)境中,這是 一個(gè)具有內(nèi)聚性的對象。
3.可維護(hù)性和易變更性
在前面《如何在struts+spring+hibernate的框架下 構(gòu)建低耦合高內(nèi)聚的軟件》中我提到,我們現(xiàn)在的軟件是在不斷變更的,這種變更不僅來自于我們的客戶,更來自于我們的市場。如果我們的軟件通過變更能及時(shí)適 應(yīng)我們的市場需求,我們就可以在市場競爭中獲勝。如何能及時(shí)變更以適應(yīng)我們的市場呢,就是通過調(diào)整軟件的結(jié)構(gòu),使我們每次的變更付出的代價(jià)最小,耗費(fèi)的人 力最小,這種變更才最快最經(jīng)濟(jì)。高內(nèi)聚的軟件,每個(gè)系統(tǒng)、模塊、類的任務(wù)都高度相關(guān),就使每一次的變更涉及的范圍縮小到最小。比如評審表發(fā)生了變更,只會 與評審表對象有關(guān),我們不會去更改其它的對象。如果我們能做到這一點(diǎn),我們的系統(tǒng)當(dāng)然是可維護(hù)性好、易變更性好的系統(tǒng)。
那么,我們?nèi)绾巫?到高內(nèi)聚呢?就拿前面我提到的評審項(xiàng)目舉例。我現(xiàn)在要為“評審表”對象編寫一段填寫并保存評審表的代碼。評審表對象的職責(zé)是更新和查詢評審表的數(shù)據(jù),但是 在顯示一個(gè)要填寫的評審表的時(shí)候,我需要顯示該評審計(jì)劃的名稱、該評審計(jì)劃有哪些評審對象需要評審。現(xiàn)在我如何編寫顯示一個(gè)要填寫的評審表的代碼?我在評 審表對象的這個(gè)相應(yīng)的函數(shù)中編寫一段查詢評審計(jì)劃和評審對象的代碼嗎?假如你這樣做了,你的代碼就不是高內(nèi)聚的,因?yàn)椴樵冊u審計(jì)劃和評審對象的數(shù)據(jù)不是它 的職責(zé)。正確的方法應(yīng)當(dāng)去請求“評審計(jì)劃”對象和“評審對象”對象來完成這些工作,而“評審表”對象只是獲取其結(jié)果。
另外,如果一個(gè)對象 要完成一個(gè)雖然在自己職責(zé)范圍內(nèi),但過程非常復(fù)雜的任務(wù)時(shí),也應(yīng)當(dāng)將該任務(wù)分解成數(shù)個(gè)功能相對獨(dú)立的子函數(shù)來完成。我曾經(jīng)看見一個(gè)朋友寫的數(shù)百行的一個(gè)函 數(shù),讓人讀起來非常費(fèi)勁。同時(shí)這樣的函數(shù)中一些相對獨(dú)立的代碼,本可以復(fù)用到其它代碼中,也變成了不可能。所以我給大家的建議是,不要寫太長的函數(shù),超過 一百行就可以考慮將一些功能分解出去。
與“低耦合”一樣,高內(nèi)聚也不是一個(gè)絕對,而是一個(gè)相對的指標(biāo),應(yīng)當(dāng)適當(dāng)而不能過度。正如我們在現(xiàn) 實(shí)生活中,如果在一個(gè)十來人的小公司,每個(gè)人的分工可能會粗一些,所分配的職責(zé)會廣一些雜一些,因?yàn)槠淇傮w的任務(wù)少;而如果在一個(gè)一兩百人的大公司,每個(gè) 人的分工會細(xì)一些,所分配的任務(wù)會更加專一些,因?yàn)榭傮w任務(wù)多,更需要專業(yè)化的分工來提高效率。軟件開發(fā)也是一樣,如果“評審計(jì)劃”對象完成的業(yè)務(wù)功能 少,并且不復(fù)雜,它完全可以代理它的子表“評審對象”和“評審者”的管理。但是“評審計(jì)劃”對象需要完成的“對評審計(jì)劃表的管理”這個(gè)基本職責(zé)包含的業(yè)務(wù) 功能繁多或者復(fù)雜,它就應(yīng)當(dāng)將“對評審對象表的管理”交給“評審對象”對象,將“對評審者表的管理”交給“評審者”對象。同樣,高內(nèi)聚的可維護(hù)性好、易變 更性好只能是一個(gè)相對的指標(biāo)。如果一個(gè)變更的確是大范圍的變更,你永遠(yuǎn)不可能通過內(nèi)聚就不進(jìn)行大范圍的變更了。同時(shí)內(nèi)聚也是要付出代價(jià)的,所以你也不必要 去為了一個(gè)不太可能的變更去進(jìn)行過度設(shè)計(jì),應(yīng)當(dāng)掌握一個(gè)度。過度的內(nèi)聚必將增加系統(tǒng)中元素之間的依賴,提高耦合度。所以“高內(nèi)聚”與“低耦合”是矛盾的,必須權(quán)衡利弊,綜合地去處理。在李洋等人翻譯的《UML和模式應(yīng)用》中,將內(nèi)聚和耦合翻譯為軟件工程中的陰與陽,是中國人對內(nèi)聚和耦合的最佳解釋。
綜上所述,“高內(nèi)聚”給軟件項(xiàng)目帶來的優(yōu)點(diǎn)是:可讀性強(qiáng)、易維護(hù)和變更、支持低耦合、移植和重用性強(qiáng)。
一個(gè)優(yōu)秀軟件開發(fā)人員的必修課:GRASP(2)低耦合
關(guān)鍵字: 設(shè)計(jì)模式
我 偶然在google或yahoo這樣的搜索引擎搜索GRASP發(fā)現(xiàn),除了國外的網(wǎng)站,國內(nèi)網(wǎng)站多介紹和討論GoF而很少介紹GRASP,即使這少量的文章 也講解非常粗略。個(gè)人認(rèn)為作為優(yōu)秀的開發(fā)人員,理解GRASP比GoF更重要,故寫此文章。前面我在《(原創(chuàng))一個(gè)優(yōu)秀軟件開發(fā)人員的必修課:GRASP 軟件開發(fā)模式淺析》中介紹了使用GRASP的目的,今天允許我調(diào)換一下順序,先從低耦合講起,因?yàn)橹T如創(chuàng)建者模式、信息專家模式的根本目的就是降低耦合。
1.低耦合(Low Coupling)
“低 耦合”這個(gè)詞相信大家已經(jīng)耳熟能詳,我們在看spring的書籍、MVC的數(shù)據(jù)、設(shè)計(jì)模式的書籍,無處不提到“低耦合、高內(nèi)聚”,它已經(jīng)成為軟件設(shè)計(jì)質(zhì)量 的標(biāo)準(zhǔn)之一。那么什么是低耦合?耦合就是對某元素與其它元素之間的連接、感知和依賴的量度。這里所說的元素,即可以是功能、對象(類),也可以指系統(tǒng)、子 系統(tǒng)、模塊。假如一個(gè)元素A去連接元素B,或者通過自己的方法可以感知B,或者當(dāng)B不存在的時(shí)候就不能正常工作,那么就說元素A與元素B耦合。耦合帶來的 問題是,當(dāng)元素B發(fā)生變更或不存在時(shí),都將影響元素A的正常工作,影響系統(tǒng)的可維護(hù)性和易變更性。同時(shí)元素A只能工作于元素B存在的環(huán)境中,這也降低了元 素A的可復(fù)用性。正因?yàn)轳詈系姆N種弊端,我們在軟件設(shè)計(jì)的時(shí)候努力追求“低耦合”。低耦合就是要求在我們的軟件系統(tǒng)中,某元素不要過度依賴于其它元素。請 注意這里的“過度”二字。系統(tǒng)中低耦合不能過度,比如說我們設(shè)計(jì)一個(gè)類可以不與JDK耦合,這可能嗎?除非你不是設(shè)計(jì)的Java程序。再比如我設(shè)計(jì)了一個(gè) 類,它不與我的系統(tǒng)中的任何類發(fā)生耦合。如果有這樣一個(gè)類,那么它必然是低內(nèi)聚(關(guān)于內(nèi)聚的問題我隨后討論)。耦合與內(nèi)聚常常是一個(gè)矛盾的兩個(gè)方面。最佳 的方案就是尋找一個(gè)合適的中間點(diǎn)。
哪些是耦合呢?
1.元素B是元素A的屬性,或者元素A引用了元素B的實(shí)例(這包括元素A調(diào)用的某個(gè)方法,其參數(shù)中包含元素B)。
2.元素A調(diào)用了元素B的方法。
3.元素A直接或間接成為元素B的子類。
4.元素A是接口B的實(shí)現(xiàn)。
幸 運(yùn)的是,目前已經(jīng)有大量的框架幫助我們降低我們系統(tǒng)的耦合度。比如,使用struts我們可以應(yīng)用MVC模型,使頁面展現(xiàn)與業(yè)務(wù)邏輯分離,做到了頁面展現(xiàn) 與業(yè)務(wù)邏輯的低耦合。當(dāng)我們的頁面展現(xiàn)需要變更時(shí),我們只需要修改我們的頁面,而不影響我們的業(yè)務(wù)邏輯;同樣,我們的業(yè)務(wù)邏輯需要變更的時(shí)候,我們只需要 修改我們的java程序,與我們的頁面無關(guān)。使用spring我們運(yùn)用IoC(反向控制),降低了業(yè)務(wù)邏輯中各個(gè)類的相互依賴。假如類A因?yàn)樾枰δ蹻而 調(diào)用類B,在通常的情況下類A需要引用類B,因而類A就依賴于類B了,也就是說當(dāng)類B不存在的時(shí)候類A就無法使用了。使用了IoC,類A調(diào)用的僅僅是實(shí)現(xiàn) 了功能F的接口的某個(gè)類,這個(gè)類可能是類B,也可能是另一個(gè)類C,由spring的配置文件來決定。這樣,類A就不再依賴于類B了,耦合度降低,重用性提 高了。使用hibernate則是使我們的業(yè)務(wù)邏輯與數(shù)據(jù)持久化分離,也就是與將數(shù)據(jù)存儲到數(shù)據(jù)庫的操作分離。我們在業(yè)務(wù)邏輯中只需要將數(shù)據(jù)放到值對象 中,然后交給hibernate,或者從hibernate那里得到值對象。至于用Oracle、MySQL還是SQL Server,如何執(zhí)行的操作,與我無關(guān)。
但是,作為優(yōu)秀的開發(fā)人員,僅僅依靠框架提供的降低軟件耦合的方法是遠(yuǎn)遠(yuǎn)不夠的。根據(jù)我的經(jīng)驗(yàn),以下一些問題我們應(yīng)當(dāng)引起注意:
1)
根據(jù)可能的變化設(shè)計(jì)軟件
我 們采用職責(zé)驅(qū)動設(shè)計(jì),設(shè)計(jì)中盡力做到“低耦合、高內(nèi)聚”的一個(gè)非常重要的前提是,我們的軟件是在不斷變化的。如果沒有變化我們當(dāng)然就不用這么費(fèi)勁了;但是 如果有變化,我們希望通過以上的設(shè)計(jì),使我們在適應(yīng)或者更改這樣的變化的時(shí)候,付出更小的代價(jià)。這里提供了一個(gè)非常重要的信息是,我們努力降低耦合的是那 些可能發(fā)生變更的地方,因?yàn)榻档婉詈鲜怯写鷥r(jià)的,是以增加資源耗費(fèi)和代碼復(fù)雜度為代價(jià)的。如果系統(tǒng)中某些元素不太可能變更,或者降低耦合所付出的代價(jià)太 大,我們當(dāng)然就應(yīng)當(dāng)選擇耦合。有一次我試圖將我的表現(xiàn)層不依賴于struts,但發(fā)現(xiàn)這樣的嘗試代價(jià)太大而失去意義了。對于軟件可能變更的部分,我們應(yīng)當(dāng) 努力去降低耦合,這就給我們提出一個(gè)要求是,在軟件設(shè)計(jì)的時(shí)候可以預(yù)判日后的變化。根據(jù)以往的經(jīng)驗(yàn)我認(rèn)為,一個(gè)軟件的業(yè)務(wù)邏輯和采用的技術(shù)框架往往是容易 變化的2個(gè)方面??蛻粜枨笞兏俏覀冘浖O(shè)計(jì)必須考慮的問題。在RUP的開發(fā)過程中,為什么需要將分析設(shè)計(jì)的過程分為分析模型和設(shè)計(jì)模型,愚以為,從分析 模型到設(shè)計(jì)模型的過程實(shí)際上是系統(tǒng)從滿足直接的客戶需求到優(yōu)化系統(tǒng)結(jié)構(gòu)、適應(yīng)可預(yù)見的客戶需求變更的一個(gè)過程。這種客戶需求的變更不僅僅指對一個(gè)客戶需求 的變更,更是指我們的軟件從適應(yīng)一個(gè)客戶需求到適應(yīng)更多客戶需求的過程。另一個(gè)方面,現(xiàn)在技術(shù)變更之快,EJB、hibernate、spring、ajax,一個(gè)一個(gè)的技術(shù)像走馬燈一樣從我們腦海中滑過,我們真不知道明天我在用什么。在這樣的情況下,適應(yīng)變化就是我們最佳的選擇。
2)
合理的職責(zé)劃分
合 理的職責(zé)劃分,讓系統(tǒng)中的對象各司其職,不僅是提高內(nèi)聚的要求,同時(shí)也可以有效地降低耦合。比如評審計(jì)劃BUS、評審表BUS、評審報(bào)告BUS都需要通過 評審計(jì)劃DAO去查詢一些評審計(jì)劃的數(shù)據(jù),如果它們都去直接調(diào)用評審計(jì)劃DAO(如圖A),則評審計(jì)劃BUS、評審表BUS、評審報(bào)告BUS三個(gè)對象都與 評審計(jì)劃DAO耦合,評審計(jì)劃DAO一旦變更將與這三個(gè)對象都有關(guān)。在這個(gè)實(shí)例中,實(shí)際上評審計(jì)劃BUS是信息專家(關(guān)于信息專家模式我將在后面討論),評審表BUS和評審報(bào)告BUS如果需要獲得評審計(jì)劃的數(shù)據(jù),應(yīng)當(dāng)向評審計(jì)劃BUS提出需求,由評審計(jì)劃BUS提供數(shù)據(jù)(如圖B)。經(jīng)過這樣的調(diào)整,系統(tǒng)的 耦合度就降低了。
3)
使用接口而不是繼承
通過對耦合的分析,我們不難發(fā)現(xiàn),繼承就是一種耦合。如果子類A繼承了父 類B,不論是直接或間接的繼承,子類A都必將依賴父類B。子類A必須使用在存在父類B的環(huán)境中,父類B不存在子類A就不能使用,這樣將影響子類A的可移植 性。一旦父類B發(fā)生任何變更,更改或去掉一個(gè)函數(shù)名,或者改變一個(gè)函數(shù)的參數(shù),都將導(dǎo)致子類A不得不變更,甚至重寫。假如父類B的子類數(shù)十上百個(gè),甚至貫 穿這個(gè)項(xiàng)目各個(gè)模塊,這樣的變更是災(zāi)難性的。這種情況最典型的例子是我們現(xiàn)在使用hibernate和spring設(shè)計(jì)DAO對象的方式,具體的描述參見 我寫的《如何在 struts + spring + hibernate的框架下構(gòu)建低耦合高內(nèi)聚的軟件結(jié)構(gòu)》一文。
總之,“低耦合”給軟件項(xiàng)目帶來的優(yōu)點(diǎn)是:易于變更、易于重用。
對于開發(fā)者而言,耦合原則表示程序中單個(gè)的模塊應(yīng)該盡可能的獨(dú)立。
處理一個(gè)模塊時(shí),不應(yīng)該依賴另一個(gè)模塊的內(nèi)部工作。
內(nèi)聚原則是指,在一個(gè)給定的模塊內(nèi)部,所有的代碼應(yīng)該只完成一個(gè)單個(gè)的目標(biāo)。
IT界有一句很著名的口號:強(qiáng)內(nèi)聚、松耦合。
即 使是最初級的程序員,在常常的被教導(dǎo)中,他也了解了這句口號的含義:我們的程序要模塊化,模塊要完成明確的一組關(guān)聯(lián)的服務(wù)功能,要求它的各部分是相關(guān)的、有機(jī)組合起來是完整體(外部程序來看黑盒子),模塊的內(nèi)部各成分之間相關(guān)聯(lián)程度要盡可能高(強(qiáng)內(nèi)聚);而模塊與模塊之間又要求是可分拆的、少依賴的(松耦 合)。
人們易于實(shí)現(xiàn)強(qiáng)內(nèi)聚的模塊,例如:一個(gè)函數(shù)實(shí)現(xiàn)一個(gè)獨(dú)立的功能,這就是強(qiáng)內(nèi)聚。
人們不易實(shí)現(xiàn)松耦合,因?yàn)?,?獨(dú)的模塊毫無意義,只有模塊間的相互協(xié)調(diào)地工作,才能實(shí)現(xiàn)系統(tǒng)的目的。而對于模塊間的相互關(guān)系的設(shè)計(jì),沒有一定的經(jīng)驗(yàn)是難以把握。耦合的強(qiáng)度依賴于:(1)一個(gè)模塊對另一個(gè)模塊的調(diào)用;(2)一個(gè)模塊向另一個(gè)模塊傳遞的數(shù)據(jù)量;(3)一個(gè)模塊施加到另一個(gè)模塊的控制的多少;(4)模塊之間接口的復(fù)雜程 度。等等。
當(dāng)然,“強(qiáng)內(nèi)聚、松耦合”也是有矛盾的,如:內(nèi)聚性越強(qiáng),則要求的函數(shù)越多(每個(gè)函數(shù)只作一件“事”),這樣,將它們組合成 “大”的功能,也就越復(fù)雜,就不可能達(dá)到松耦合。因此,應(yīng)在二者之間作出平衡與折衷的選擇,這也體現(xiàn)程序員的水平。從系統(tǒng)論的角度來看,系統(tǒng)是有層次的,即系統(tǒng)可以分為子系統(tǒng),模塊可分為子模塊,“強(qiáng)內(nèi)聚、松耦合”的“度”的把握,應(yīng)結(jié)合系統(tǒng)的次層性來考慮,即通常應(yīng)在層次性上作出折衷,如:模塊內(nèi)子程序(下一個(gè)層次上)應(yīng)共享數(shù)據(jù)(有一定的耦合度),而減少全局變量能降低子程序性間的耦合性。
面向?qū)ο蟮恼Z言進(jìn)一步強(qiáng)化了“強(qiáng)內(nèi)聚、松耦 合”,類的封裝性既強(qiáng)調(diào)了相關(guān)內(nèi)容(數(shù)據(jù)及其操作)的內(nèi)聚,又強(qiáng)調(diào)了類的獨(dú)立性和私密性。而類的繼承性以及友元等,就是在松耦合的原則下規(guī)范了類之間的關(guān) 聯(lián)關(guān)系。類與類之間通常通過接口的契約實(shí)現(xiàn)服務(wù)提供者/服務(wù)請求者模式,這就是典型的松耦合。
“強(qiáng)內(nèi)聚、松耦合”對于程序編寫分工、程 序的可維護(hù)性以及測試都有重要的關(guān)系,如:從設(shè)計(jì)角度來看,在“強(qiáng)內(nèi)聚、松耦合”的指導(dǎo)下進(jìn)行的設(shè)計(jì)得到的程序模塊,符合項(xiàng)目管理的WBS(工作分解結(jié) 構(gòu))的要求,其相對獨(dú)立的模塊可以分配到具體的程序員進(jìn)行開發(fā),另外,程序編碼外包也必須建立在這種原則的設(shè)計(jì)之下;從程序生命期角度來看,它有利于提高 程序質(zhì)量,特別是方便于程序的日后維護(hù),即程序模塊的相對獨(dú)立性是可維護(hù)性的保證;再從測試角度來看,符合“強(qiáng)內(nèi)聚、松耦合”的程序,易于對局部(模塊)進(jìn)行黑盒測試,也易于編寫測試用的“樁”和“驅(qū)動”。
“強(qiáng)內(nèi)聚、松耦合”也是對組織結(jié)構(gòu)的要求,項(xiàng)目組分為幾個(gè)小組(正式的或非正式 的),各小組的工作應(yīng)是高度相關(guān)的,各小組之間的工作應(yīng)盡量是較少相關(guān)或有明確的接口,從而減少溝通成本。其實(shí),“強(qiáng)內(nèi)聚、松耦合”是系統(tǒng)中應(yīng)遵守的普遍 原則,我們在許多領(lǐng)域都可以找到它的應(yīng)用。
“強(qiáng)內(nèi)聚、松耦合”是我們不得不念的“三字經(jīng)”,我們一定要念好它。
高內(nèi)聚和低耦合是同義詞。
1。高內(nèi)聚,指自成一體。
2。低耦合,指對外部的依賴很小。
只是他們側(cè)重的角度不同,內(nèi)聚側(cè)重的是內(nèi)政,而耦合側(cè)重的是外交。
一個(gè)對象有兩種外交形勢,一是輸出價(jià)值觀,讓別人調(diào)用,二是輸入價(jià)值觀,依賴別人。低耦合指的是輸入價(jià)值觀應(yīng)該盡量少。如果必然要輸入,那么輸入的形式也有強(qiáng)弱之分,強(qiáng)耦合就是嚴(yán)重依賴別人。
從系統(tǒng)角度,如果要每個(gè)對象不依賴別人,卻又妄想更多的輸出價(jià)值觀,這是矛盾的。因此,所謂的低耦合,指的是盡量減少不必要的輸入,尤其避免強(qiáng)耦合。
從系統(tǒng)角度,越接近系統(tǒng)底層的,越具體的,耦合越強(qiáng),而越高層的,越抽象的,耦合越低。
上章回顧
在上篇中我們講解了幾類UML2.0語言新推出的建模圖形,總體來說通過這些圖形能更詳細(xì)的將某類信息表達(dá)出來。在這里我們簡單回顧上篇講解的內(nèi)容。
上圖中已經(jīng)簡單介紹了上章講述的內(nèi)容,具體內(nèi)容請看:系統(tǒng)架構(gòu)師-基礎(chǔ)到企業(yè)應(yīng)用架構(gòu)-系統(tǒng)建模[下篇]。
二、摘要
本章將主要的簡單介紹在系統(tǒng)架構(gòu)中的設(shè)計(jì)模式及相應(yīng)規(guī)范準(zhǔn)則。并結(jié)合相應(yīng)的代碼來說明如何遵循系統(tǒng)架構(gòu)中的一些基本的設(shè)計(jì)規(guī)范及準(zhǔn)則。而我們將在本文介
紹幾類常用的設(shè)計(jì)規(guī)范,我們先來看看結(jié)構(gòu)化設(shè)計(jì)的二個(gè)基本原則:
當(dāng)然既然提出了基本的準(zhǔn)則,那么我們?nèi)绾蝸頋M足準(zhǔn)則呢,并且能更好的設(shè)計(jì)呢?我們可以通過如下手段來達(dá)到這樣的要求:
當(dāng)然圖中演示了功能分離的策略,通過把需求按照不同的功能詳細(xì)的劃分開來,每個(gè)功能都是獨(dú)立 的,當(dāng)然我們這里也可以成為是關(guān)注點(diǎn)。下面我們將針對這些原則進(jìn)行詳細(xì)的講解。
三、本章內(nèi)容
1、上章回顧。
2、摘要。
3、本章內(nèi)容。
4、設(shè)計(jì)規(guī)范及原則。
5、如何滿足設(shè)計(jì)要求。
6、本章總結(jié)。
7、系列進(jìn)度。
8、下篇預(yù)告。
四、設(shè)計(jì)規(guī)范及準(zhǔn)則。
1、高內(nèi)聚
首先我們來看看內(nèi)聚的含義:軟件含義上的內(nèi)聚其實(shí)是從化學(xué)中的分子的內(nèi)聚演變過來的,化學(xué)中的分子間的作用力,作用力強(qiáng)則表現(xiàn)為內(nèi)聚程度高。在軟件中內(nèi)
聚程度的高低,標(biāo)識著軟件設(shè)計(jì)的好壞。
我們在進(jìn)行架構(gòu)設(shè)計(jì)時(shí)的內(nèi)聚高低是指,設(shè)計(jì)某個(gè)模塊或者關(guān)注點(diǎn)時(shí),模塊或關(guān)注點(diǎn)內(nèi)部的一系列相關(guān)功能的相關(guān)程度的高低。
例如:下單模塊:
一般情況下,下單模塊都會有如下的信息,訂單的信息,產(chǎn)品的信息及誰下的單(買家信息)。這是基
本的,那么我們設(shè)計(jì)的時(shí)候就要把相關(guān)的功能內(nèi)聚到一起。當(dāng)然這是從大功能(下單管理)上來說,當(dāng)然這些模塊還可以再細(xì)化分成產(chǎn)品、訂單、會員等子模塊。
例如我們在設(shè)計(jì)數(shù)據(jù)庫操作輔助類提供的方法有:
通過這樣的方式,那么這個(gè)組件只負(fù)責(zé)數(shù)據(jù)庫操作。這樣帶來的好處也是顯而易見的。高內(nèi)
聚提供了更好的可維護(hù)性和可復(fù)用性。而低內(nèi)聚的模塊則表名模塊直接的依賴程度高,那么一旦修改了該模塊依賴的對象則無法使用該模塊,必須也進(jìn)行相應(yīng)的修改才
可以繼續(xù)使用。
低內(nèi)聚的模塊設(shè)計(jì)的壞處有:首先模塊的功能不單一,模塊的職責(zé)不明確,比較松散,更有甚者是完成不相關(guān)的功能。這樣的設(shè)計(jì)往往是不可取的??梢酝ㄟ^重
構(gòu)來完善。
下面我們來說下高內(nèi)聚的簡單解釋:什么樣的模塊算是高內(nèi)聚,并且能夠在系統(tǒng)中很好的使用。
那么我們在設(shè)計(jì)的過程中如何去完成高內(nèi)聚呢?
以上基本上講述了高內(nèi)聚的好處,并且闡述了如何實(shí)現(xiàn)高內(nèi)聚的步驟和原則。下面我們來說說可能高內(nèi)聚帶來的壞處。
高內(nèi)聚有時(shí)候也不是說所有的情況都采用這樣的原則,當(dāng)然高內(nèi)聚還是要適度的,下面來舉例說明:例如內(nèi)聚性要求強(qiáng)的話就像Windows32中系統(tǒng)提供的
API,里面的函數(shù)太多了,都放在一個(gè)Dll中,那么每個(gè)函數(shù)完成一個(gè)功能。這樣強(qiáng)大的功能,會比較復(fù)雜,所以并不是完全的高內(nèi)聚越高越好,還是要看實(shí)際的需要。
當(dāng)然維護(hù)起來也不是特別的方便。
2、低耦合
首先我們來看看低耦合的定義:低耦合是用來度量模塊與模塊直接的依賴關(guān)系。耦合當(dāng)然也可以這樣簡單的理解,我想懂電腦的應(yīng)該都知道,CPU與主板之間的
關(guān)系,CPU如果是特殊的CPU必須使用特殊的主板來支持,那么如果說這個(gè)CPU不唯一依賴唯一主板,那么就認(rèn)為這個(gè)CPU與主板的關(guān)系是低耦合的關(guān)系。
下面我們來舉例說明低耦合的設(shè)計(jì)與高耦合的設(shè)計(jì):
這是一個(gè)簡單的低耦合的設(shè)計(jì),電器與插座之間是低耦合的關(guān)系,就算我替換了不同的插座,電器依
然可以正常的工作。因此簡單的描述如下,就是A模塊與B模塊存在依賴關(guān)系,那么當(dāng)B發(fā)生改變時(shí),A模塊仍然可以正常工作,那么就認(rèn)為A與B是低耦合的。
1、筆記本接音響可以正常的使用。
2、筆記本接專配耳機(jī)正常的使用。
對應(yīng)一般的音響來說,筆記本是通用的,音響和筆記本直接的關(guān)系是低耦合的,但是筆記本和耳機(jī)卻是高耦合的,只有專配的耳機(jī)才能和筆記本互聯(lián)使用,而不
是通用的,所以說筆記本和專配耳機(jī)存在著較強(qiáng)的依賴關(guān)系。當(dāng)然最簡單的方式就是筆記本提供統(tǒng)一的耳機(jī)接口,可以滿足一般性的需求。
下面我們將來分析如何構(gòu)建低耦合的設(shè)計(jì)。
總結(jié)
上面我們已經(jīng)講解了低耦合和高內(nèi)聚的二個(gè)原則,通過這2個(gè)原則我們知道,滿足這2個(gè)原則是衡量一個(gè)架構(gòu)設(shè)計(jì)好壞的一個(gè)參考標(biāo)準(zhǔn)。下面我們將來講解通過
功能分離的方式來滿足上面的2個(gè)原則。
五、如何滿足設(shè)計(jì)要求
1、如何按功能進(jìn)行模塊化的分離。
我們在將一個(gè)系統(tǒng)進(jìn)行功能劃分時(shí),我們一般如下來進(jìn)行:
首先、我們先把功能職責(zé)劃分成獨(dú)立的單元。
例如現(xiàn)在有個(gè)B2C系統(tǒng),那么我們按照B2C的需求,如下分析:
我們這里簡單的分析下B2C應(yīng)該具有的功能模塊,當(dāng)然這些模塊的劃分中,有些模
塊還可以繼續(xù)的分離,當(dāng)然我這里只是實(shí)例分析出來。
2、對分離出來的模塊化進(jìn)行抽象,例如我們以支付為例。
這里通過支付接口向外提供服務(wù)。那么外界模塊不關(guān)心支付系統(tǒng)模塊的變化,只需要調(diào)用接口
即可,如果具體的支付方式,比如支付寶的方式發(fā)生改變,在調(diào)用支付服務(wù)的模塊中也不需要做任何的修改就可以正常的提供服務(wù)。顯然這樣的方式是不錯(cuò)的實(shí)現(xiàn)方
式。
通常情況下我們在系統(tǒng)分離式只是以接口的方式提供服務(wù),供其他的模塊進(jìn)行使用。在模塊內(nèi)部有大量的信息是不要向外部暴露的,所以模塊在設(shè)計(jì)時(shí)訪問域的定
義就要劃分好,防止因?yàn)樵L問域的定義而對模塊的信息造成破壞。
下面我們來看下功能分離在不同的設(shè)計(jì)理念下都是什么樣的表現(xiàn):
上面只是實(shí)體性的分析了功能分離的好處及應(yīng)用的廣度,當(dāng)然我們在后續(xù)會結(jié)合實(shí)例來講解如何來實(shí)現(xiàn)這樣的軟件設(shè)計(jì)模式。當(dāng)然這只是軟件的架構(gòu)設(shè)計(jì),那么如
果細(xì)化到具體的實(shí)現(xiàn)呢?我們?nèi)绾稳ピO(shè)計(jì)每個(gè)功能點(diǎn)呢?這就是下章我們要講解的內(nèi)容了,那么本文先列出二種常見的方式。