【關(guān)鍵字】設(shè)計(jì)模式的原則 設(shè)計(jì)模式 uml類圖 一站式消化吸收學(xué)習(xí)
詞匯學(xué)習(xí)
IoC (Inversion of Control) 控制反轉(zhuǎn)
依賴注入(DI)
【引用】
【設(shè)計(jì)模式】http://www.uml.org.cn/sjms/201211023.asp
【x-a關(guān)系說(shuō)明】http://blog.csdn.net/cbk861110/article/details/9028189
【uml類圖】http://www.cnblogs.com/alex-blog/articles/2704214.html
【設(shè)計(jì)模式大的分類】http://blog.csdn.net/jason0539/article/details/44956775
【各種工廠模式】http://www.cnblogs.com/toutou/p/4899388.html
一、uml類圖
UML類圖的標(biāo)記語(yǔ)言我都忘了我記了多少遍匀们,忘了多少遍了昆著!只有捋出來(lái)頭緒和關(guān)聯(lián),才可能永遠(yuǎn)記住并融入你的思維方式,沒(méi)有任何聯(lián)系的東西,我們大腦是不擅長(zhǎng)處理的!
總的來(lái)說(shuō)我們的類圖分為兩大類
is-a和has-a
is-a 可以分為繼承和實(shí)現(xiàn)壮虫,依據(jù):是否有實(shí)體函數(shù)實(shí)現(xiàn)來(lái)繼承,如果虛函數(shù)环础,則為實(shí)現(xiàn)
has-a:包括四種關(guān)聯(lián)關(guān)系的旨指,組合,聚合喳整,關(guān)聯(lián)和依賴,依據(jù)關(guān)系強(qiáng)弱排名
-
is a 繼承/泛化和實(shí)現(xiàn)
圖標(biāo):空心三角箭頭裸扶,叫上實(shí)線或者虛線框都,父親有遺產(chǎn),我就很實(shí)在呵晨,真心實(shí)意魏保,如果父親啥也沒(méi)有,是接口摸屠,都是虛的谓罗,只有理想讓我繼承,那么我來(lái)實(shí)現(xiàn)季二,我的線就是虛的檩咱。
繼承和實(shí)現(xiàn)都是is-a揭措,但是如果父類是接口類,那么就是實(shí)現(xiàn)了刻蚯,因?yàn)榻涌陬惓颂摵瘮?shù)绊含,啥也沒(méi)有,沒(méi)有遺產(chǎn)的父親炊汹,怎么繼承呢躬充?所以叫實(shí)現(xiàn),父親沒(méi)有遺產(chǎn)讨便,我來(lái)實(shí)現(xiàn)父親的遺愿充甚,哈哈
組合>聚合>關(guān)聯(lián)>依賴;
通過(guò)類圖霸褒,可以發(fā)現(xiàn)圖的規(guī)律
通過(guò)圖的變化關(guān)系伴找,可以發(fā)現(xiàn),關(guān)系性越強(qiáng)的傲霸,箭頭內(nèi)容就越多疆瑰,比如組合,實(shí)線昙啄,有實(shí)心菱形穆役,關(guān)系越弱的,箭頭內(nèi)容越少梳凛,比如依賴耿币,虛線,沒(méi)有菱形韧拒。簡(jiǎn)直是大發(fā)現(xiàn)!
下面的幾個(gè)都是contain-a淹接,has-a,relate-a,rely-a
- contains-a 組合關(guān)系叛溢,包含關(guān)系塑悼,強(qiáng)聚合
組合關(guān)系是局部和整體不可分開(kāi)的,腦仁和腦袋的關(guān)系楷掉,分開(kāi)了就不好了厢蒜,要死人的!
我和我的大腦也是這個(gè)關(guān)系烹植,不能缺少局部單元斑鸦;
- has a 聚合關(guān)系
是可以分割的,比如一袋子食品墩虹,袋子里裝著饅頭和包子嘱巾,那么禮物袋和饅頭包子就是聚合憨琳,隨時(shí)可以分離
- 關(guān)聯(lián)
長(zhǎng)期關(guān)系浓冒,比如朋友栽渴,同事關(guān)系,都是長(zhǎng)期性的稳懒,非臨時(shí)性的
-
依賴
二场梆、設(shè)計(jì)模式原則-六大原則
- 六脈神劍墅冷,要幫我們干掉的毒性代碼
new(對(duì)象創(chuàng)建)是有毒的
public class MovieLister {
private MovieFinder finder;
public MovieLister() {
finder = new MovieFinderImpl();
}
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
...
}
2.1、設(shè)計(jì)原則 深度理解【此小節(jié)引用知乎的一個(gè)問(wèn)答】
https://www.zhihu.com/question/19582024
那些年或油,空氣中仿佛還能聞到漢唐盛世的余韻寞忿,因此你決不允許自己的臉上有油光,時(shí)刻保持活力顶岸。然而岔乔,你一定曾為這些“高深術(shù)語(yǔ)”感到過(guò)困擾席赂。也許時(shí)至今日虱歪,你仍對(duì)它們一知半解谴咸。不過(guò)就在今天,這一切都將徹底改變卷谈!我將帶領(lǐng)你以一種全新的高清視角進(jìn)入奇妙的編程世界杯拐,領(lǐng)略涵泳在這些“高深術(shù)語(yǔ)”中的活潑潑的地氣,以及翩躚于青萍之末的云水禪心世蔗。
·內(nèi)聚
內(nèi)聚端逼,通俗的來(lái)講,就是自己的東西自己保管污淋,自己的事情自己做顶滩。
經(jīng)典理論告訴我們,程序的兩大要素:一個(gè)是數(shù)據(jù)(data)寸爆,一個(gè)是操作(opration)诲祸。而 PASCAL之父Nicklaus Wirth則進(jìn)一步提出了“程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法”的著名公式。雖然提法上有所差異而昨,但是其根本內(nèi)涵卻是一致的,微妙的差別在于找田,“數(shù)據(jù) + 操作”是微觀的視域歌憨,“數(shù)據(jù)結(jié)構(gòu) + 算法”則是中觀的視域。而在宏觀的視域下墩衙,我認(rèn)為“程序 = 對(duì)象 + 消息”务嫡。對(duì)象是什么甲抖?對(duì)象就是保管好自己的東西,做好自己的事情的程序模塊——這就是內(nèi)聚心铃!傳統(tǒng)的面向過(guò)程編程方法由于割裂了數(shù)據(jù)結(jié)構(gòu)和算法准谚,使得軟件的內(nèi)聚性普遍低迷,曾一度引發(fā)了軟件危機(jī)去扣。試想柱衔,大家都自己的東西不好好保管,自己的事情也不好好做愉棱,不引發(fā)危機(jī)才怪呢唆铐!當(dāng)然,對(duì)象的內(nèi)聚只是內(nèi)聚的一個(gè)層次奔滑,在不同的尺度下其實(shí)都有內(nèi)聚的要求艾岂,比如方法也要講內(nèi)聚,架構(gòu)也要講內(nèi)聚朋其。
《周易·彖傳》中講“乾道變化王浴,各正性命,保合太和梅猿,乃利貞”氓辣,就是要求每一個(gè)個(gè)體因循著各自的稟賦而努力成就各自的品性,然后各自保全粒没,彼此和合筛婉,最終達(dá)成宇宙的完滿狀態(tài)●桑《論語(yǔ)·憲問(wèn)》中爽撒,子路問(wèn)君子。子曰:“修己以敬响蓉∷段穑”曰:“如斯而已乎?”曰:“修己以安人”枫甲,更是明確的教導(dǎo)我們要不斷提高自身的內(nèi)聚性源武,最大限度地減少給他人造成的麻煩,從而達(dá)到安人想幻、安百姓粱栖、安天下的目標(biāo)。我想脏毯,成長(zhǎng)的過(guò)程就是一個(gè)不斷提升內(nèi)聚的過(guò)程闹究。“自己的東西自己保管食店,自己的事情自己做”渣淤,這些孩提時(shí)代的教誨赏寇,放到今天仍能讓不少“大人”臉紅不已。太多的人保管不好自己的“東西”价认,保管不好自己的身體嗅定,保管不好自己的婚姻,更保管不好自己如蛛絲般震顫飄蕩的狂亂的心用踩。至于做好自己的事情渠退,則更是惘然,甚至很多人連自己的事情是什么都搞不清楚捶箱,因此渾渾噩噩智什,飽食終日。內(nèi)聚丁屎,是一個(gè)值得我們好好反思的問(wèn)題荠锭。
·依賴·耦合
在面向?qū)ο缶幊讨校瑢?duì)象自身是內(nèi)聚的晨川,是保管好自己的數(shù)據(jù)证九,完成好自己的操作的,而對(duì)外界呈現(xiàn)出自己的狀態(tài)和行為共虑。但是愧怜,沒(méi)有絕對(duì)的自力更生,對(duì)外開(kāi)放也是必要的妈拌!一個(gè)對(duì)象拥坛,往往需要跟其他對(duì)象打交道,既包括獲知其他對(duì)象的狀態(tài)尘分,也包括仰賴其他對(duì)象的行為猜惋,而一旦這樣的事情發(fā)生時(shí),我們便稱該對(duì)象依賴于另一對(duì)象培愁。只要兩個(gè)對(duì)象之間存在一方依賴一方的關(guān)系著摔,那么我們就稱這兩個(gè)對(duì)象之間存在耦合。 比如媽媽和baby定续,媽媽要隨時(shí)關(guān)注baby的睡谍咆、醒、困私股、哭摹察、尿等等狀態(tài),baby則要仰賴媽媽的喂奶倡鲸、哄睡港粱、換紙尿褲等行為,從程序的意義上說(shuō),二者互相依賴查坪,因此也存在耦合。首先要說(shuō)宁炫,耦合是必要的偿曙。我們來(lái)看以下這個(gè)實(shí)驗(yàn)。
【王陽(yáng)明與山中之花】
View Code
由于王陽(yáng)明這個(gè)對(duì)象不依賴山花這個(gè)對(duì)象羔巢,又沒(méi)有其他的方式來(lái)獲知山花的盛開(kāi)狀態(tài)望忆,所以他要么選擇不說(shuō),要么瞎說(shuō)竿秆,但不說(shuō)編譯是通不過(guò)启摄,而瞎說(shuō)作為王陽(yáng)明來(lái)講也是通不過(guò)的,所以這個(gè)系統(tǒng)是無(wú)法成立的幽钢。要想系統(tǒng)成立歉备,必須要這樣寫:
public bool AdmireFlowers()
{
return flower.IsBloomed; ;
}
無(wú)論這個(gè)山花對(duì)象是怎么來(lái)的,作為參數(shù)傳入還是作為屬性設(shè)置匪燕、還是在內(nèi)部構(gòu)造出來(lái)蕾羊,總之,王陽(yáng)明與山花之間發(fā)生了依賴帽驯,二者之間產(chǎn)生了耦合龟再。 當(dāng)然,這是一個(gè)很淺顯的問(wèn)題尼变。有趣的是王陽(yáng)明對(duì)此事的看法:“你未看花時(shí)利凑,花與你同寂;你來(lái)看花嫌术,花于你則一時(shí)分明起來(lái)哀澈。可見(jiàn)心外無(wú)物蛉威!”王陽(yáng)明講的是對(duì)的日丹!“心外無(wú)物”翻譯技術(shù)語(yǔ)言是這樣的:不存在耦合的兩個(gè)對(duì)象必然拿不到對(duì)方的引用!
·耦合度·解耦和
耦合的程度就是耦合度蚯嫌,也就是雙方依賴的程度哲虾。上文所說(shuō)的媽媽和baby就是強(qiáng)耦合。而你跟快遞小哥之間則是弱耦合择示。一般來(lái)說(shuō)耦合度過(guò)高并不是一件好事束凑。就拿作為IT精英的你來(lái)說(shuō)吧,上級(jí)隨時(shí)敦促你的工作進(jìn)度栅盲,新手頻繁地需要你指導(dǎo)問(wèn)題汪诉,隔三差五還需要參加酒局飯局,然后還要天天看領(lǐng)導(dǎo)的臉色、關(guān)注老婆的心情扒寄,然后你還要關(guān)注代碼中的bug 鱼鼓、bug、bug该编,和需求的變化迄本、變化、變化课竣,都?jí)蚪诡^爛額了嘉赎,還猝不及防的要關(guān)注眼睛、頸椎于樟、前列腺和頭發(fā)的狀態(tài)公条,然后你再炒個(gè)股,這些加起來(lái)大概就是個(gè)強(qiáng)耦合了迂曲。從某種意義上來(lái)說(shuō)靶橱,耦合天生就與自由為敵,無(wú)論是其他對(duì)象依賴于你奢米,還是你依賴其他對(duì)象抓韩。比如有人嗜煙、酗酒鬓长,你有多依賴它們就有多不自由谒拴;比如有人家里生了七八個(gè)娃,還有年邁的父母涉波、岳父母英上,他們有多依賴你,你就有多不自由啤覆。所以老子這樣講:“五音令人耳聾苍日,五色令人目盲,馳騁狩獵令人心發(fā)狂窗声,難得之貨令人行妨相恃。”盧梭也是不無(wú)悲涼的說(shuō)“人生而自由笨觅,卻又無(wú)往而不在枷鎖中”拦耐。因此,要想自由见剩,就必須要降低耦合杀糯,而這個(gè)過(guò)程就叫做解耦和。
·依賴倒置(Dependence Inversion Principle)
解耦和最重要的原則就是依賴倒置原則:
高層模塊不應(yīng)該依賴底層模塊苍苞,他們都應(yīng)該依賴抽象固翰。抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。
《資本論》中都曾闡釋依賴倒轉(zhuǎn)原則——在商品經(jīng)濟(jì)的萌芽時(shí)期骂际,出現(xiàn)了物物交換疗琉。假設(shè)你要買一個(gè)IPhone,賣IPhone的老板讓你拿一頭豬跟他換歉铝,可是你并沒(méi)有養(yǎng)豬没炒,你只會(huì)編程。所以你找到一位養(yǎng)豬戶犯戏,說(shuō)給他做一個(gè)養(yǎng)豬的APP來(lái)?yè)Q他一頭豬,他說(shuō)換豬可以拳话,但是得用一條金項(xiàng)鏈來(lái)?yè)Q——所以這里就出現(xiàn)了一連串的對(duì)象依賴先匪,從而造成了嚴(yán)重的耦合災(zāi)難。解決這個(gè)問(wèn)題的最好的辦法就是弃衍,買賣雙發(fā)都依賴于抽象——也就是貨幣——來(lái)進(jìn)行交換呀非,這樣一來(lái)耦合度就大為降低了。
再舉一個(gè)編程中的依賴倒置的例子镜盯。我們知道岸裙,在通信中,消息的收發(fā)和消息的處理往往密不可分速缆。就一般的通信框架而言降允,消息的收發(fā)通常是已經(jīng)實(shí)現(xiàn)了的,而消息的處理則是需要用戶來(lái)自定義完成的艺糜。先看一個(gè)正向依賴的例子:<u>輕量級(jí)通信引擎StriveEngine</u>剧董。tcpServerEngine是StriveEngine.dll提供通信引擎,它發(fā)布有一個(gè)MessageReceived事件破停。假設(shè)我定義了一個(gè)CustomizeHandler類來(lái)用于消息處理翅楼,那么CustomizeHandler的內(nèi)部需要預(yù)定tcpServerEngine的MessageReceived事件,因此customizeHandler依賴于tcpServerEngine真慢,這就是一個(gè)普通的依賴關(guān)系毅臊,也就是高層模塊依賴于低層模塊。
而ESFramework通信框架則應(yīng)用了依賴倒轉(zhuǎn)原則黑界。ESFramework定義了一個(gè)IcustomizeHandler接口管嬉,用戶在進(jìn)行消息處理時(shí),實(shí)現(xiàn)該接口园爷,然后將其注入到rapidPassiveEngine客戶端通信引擎之中宠蚂。
View Code
很明顯,相比于上一個(gè)例子童社,這里的依賴關(guān)系變成了rapidPassiveEngine依賴于customizeHandler求厕,也就是說(shuō)依賴關(guān)系倒置了過(guò)來(lái),上層模塊不再依賴于底層模塊,而是它們共同依賴于抽象呀癣。rapidPassiveEngine依賴的是IcustomizeHandler接口類型的參數(shù)美浦,customizeHandler同樣是以實(shí)現(xiàn)的接口的方式依賴于IcustomizeHandler——這就是一個(gè)依賴倒置的典范。
控制反轉(zhuǎn)(Inversion of Control)
控制反轉(zhuǎn)跟依賴倒置是如出一轍的兩個(gè)概念项栏,當(dāng)存在依賴倒置的時(shí)候往往也存在著控制反轉(zhuǎn)浦辨。但是控制反轉(zhuǎn)也有自己的獨(dú)特內(nèi)涵。
框架將調(diào)用開(kāi)發(fā)人員的代碼沼沈,而不是其他方式流酬。該框架實(shí)際上是一個(gè)可擴(kuò)展的結(jié)構(gòu),它為開(kāi)發(fā)人員提供了一組注入自定義代碼段的切入點(diǎn)列另。
引用
https://coyee.com/article/12113-three-design-patterns-that-use-inversion-of-control-sitepoint
這篇博客:主要思想就是控制反轉(zhuǎn)依靠三種方式(以來(lái)注入芽腾,觀察者模式,模板方法)實(shí)現(xiàn)页衙,然后框架調(diào)用用戶的代碼摊滔,用戶將實(shí)現(xiàn)代碼依靠接口傳遞給框架
首先我們要區(qū)分兩個(gè)角色,server 跟 Client店乐,也就是服務(wù)方和客戶方艰躺。提供服務(wù)端的一方稱為服務(wù)方,請(qǐng)求服務(wù)的一方稱為客戶方眨八。我們最熟悉的例子就是分布式應(yīng)用的C/S架構(gòu)腺兴,服務(wù)端和客戶端。其實(shí)除此之外踪古,C/S關(guān)系處處可見(jiàn)含长。比如在TCP/IP協(xié)議棧中,我們知道伏穆,每層協(xié)議為上一層提供服務(wù)拘泞,那么這里就是一個(gè)C/S關(guān)系。當(dāng)我們使用開(kāi)發(fā)框架時(shí)枕扫,開(kāi)發(fā)框架就是作為服務(wù)方陪腌,而我們自己編寫的業(yè)務(wù)應(yīng)用就是客戶方。當(dāng)Client調(diào)用server時(shí)烟瞧,這個(gè)叫做一般的控制诗鸭;而當(dāng)server調(diào)用Client時(shí),就是我們所說(shuō)的控制反轉(zhuǎn)参滴,同時(shí)我們也將這個(gè)調(diào)用稱為“回調(diào)”强岸。控制反轉(zhuǎn)跟依賴倒置都是一種編程思想砾赔,依賴倒置著眼于調(diào)用的形式蝌箍,而控制反轉(zhuǎn)則著眼于程序流程的控制權(quán)青灼。一般來(lái)說(shuō),程序的控制權(quán)屬于server妓盲,而一旦控制權(quán)交到Client杂拨,就叫控制反轉(zhuǎn)。比如你去下館子悯衬,你是Client餐館是server弹沽。你點(diǎn)菜,餐館負(fù)責(zé)做菜筋粗,程序流程的控制權(quán)屬于server策橘;而如果你去自助餐廳,程序流程的控制權(quán)就轉(zhuǎn)到Client了娜亿,也就是控制反轉(zhuǎn)役纹。
控制反轉(zhuǎn)的思想體現(xiàn)在諸多領(lǐng)域。比如事件的發(fā)布/ 訂閱就是一種控制反轉(zhuǎn)暇唾,GOF設(shè)計(jì)模式中也多處體現(xiàn)了控制反轉(zhuǎn),比如典型的模板方法模式等辰斋。而開(kāi)發(fā)框架則是控制反轉(zhuǎn)思想應(yīng)用的集中體現(xiàn)策州。比如之前所舉的ESFramework通信框架的例子,通信引擎回調(diào)用戶自定義的消息處理器宫仗,這就是一個(gè)控制反轉(zhuǎn)够挂。以及ESFramework回調(diào)用戶自定義的群組關(guān)系和好友關(guān)系,回調(diào)用戶自定義的用戶管理器以管理在線用戶相關(guān)狀態(tài)藕夫,回調(diào)用戶自定義的登陸驗(yàn)證處理孽糖,等等不一而足。再比如與ESFramework一脈相承的輕量級(jí)通信引擎StriveEngine毅贮,通過(guò)回調(diào)用戶自定義的通信協(xié)議來(lái)實(shí)現(xiàn)更加靈活的通信办悟。
由此我們也可以總結(jié)出開(kāi)發(fā)框架與類庫(kù)的區(qū)別:使用開(kāi)發(fā)框架時(shí),框架掌握程序流程的控制權(quán)滩褥,而使用類庫(kù)時(shí)病蛉,則是應(yīng)用程序掌握程序流程的控制權(quán)」寮澹或者說(shuō)铺然,使用框架時(shí),程序的主循環(huán)位于框架中酒甸,而使用類庫(kù)時(shí)魄健,程序的主循環(huán)位于應(yīng)用程序之中〔迩冢框架會(huì)回調(diào)應(yīng)用程序沽瘦,而類庫(kù)則不會(huì)回調(diào)應(yīng)用程序革骨。ESFramework和StriveEngine中最主要的對(duì)象都以engine來(lái)命名,我們也可以看出框架對(duì)于程序主循環(huán)的控制——它會(huì)為你把握方向其垄、眼看前方苛蒲、輕松駕馭!
·依賴注入(Dependency Injection)
【啊哈】大家有沒(méi)有注意到绿满,我們的c語(yǔ)言的函數(shù)內(nèi)部就是利用形參來(lái)進(jìn)行普通的邏輯實(shí)現(xiàn)和運(yùn)算臂外,然后實(shí)際需要用到的數(shù)據(jù)以實(shí)參傳入,依賴注入就像函數(shù)的參數(shù)一樣喇颁,把實(shí)體對(duì)象或者函數(shù)指針傳入漏健,不改變框架的代碼,而框架中的代碼以 function定義的函數(shù)指針進(jìn)行邏輯實(shí)現(xiàn)橘霎,或者以形參中的對(duì)象或者類進(jìn)行實(shí)現(xiàn)蔫浆,實(shí)際使用的時(shí)候傳入外部的對(duì)象或者函數(shù)即可!道理如出一轍姐叁,很好理解瓦盛!
- 構(gòu)造函數(shù)的參數(shù)注入
public class MovieLister {
private MovieFinder finder;
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
...
}
- setter注入
public class MovieLister {
s...
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
}
- 接口注入
接口注入使用接口來(lái)提供setter方法,其實(shí)現(xiàn)方式如下
public interface InjectFinder {
void injectFinder(MovieFinder finder);
}
class MovieLister implements InjectFinder {
...
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}
...
}
依賴注入與依賴倒置外潜、控制反轉(zhuǎn)的關(guān)系仍舊是一本萬(wàn)殊原环。依賴注入,就其廣義而言处窥,即是通過(guò)“注入”的方式嘱吗,來(lái)獲得依賴。我們知道滔驾,A對(duì)象依賴于B對(duì)象谒麦,等價(jià)于A對(duì)象內(nèi)部存在對(duì)B對(duì)象的“調(diào)用”,而前提是A對(duì)象內(nèi)部拿到了B對(duì)象的引用哆致。B對(duì)象的引用的來(lái)源無(wú)非有以下幾種:A對(duì)象內(nèi)部創(chuàng)建(無(wú)論是作為字段還是作為臨時(shí)變量)绕德、構(gòu)造器注入、屬性注入摊阀、方法注入迁匠。后面三種方式統(tǒng)稱為“依賴注入”,而第一種方式我也生造了一個(gè)名詞驹溃,稱為“依賴內(nèi)生”城丧,二者根本的差異即在于,我所依賴的對(duì)象的創(chuàng)建工作是否由我自己來(lái)完成豌鹤。當(dāng)然亡哄,這個(gè)是廣義的依賴注入的概念,而我們一般不會(huì)這樣來(lái)使用布疙。我們通常使用的蚊惯,是依賴注入的狹義的概念愿卸。不過(guò),直接陳述其定義可能會(huì)過(guò)于詰屈聱牙截型,我們還是從具體的例子來(lái)看趴荸。
比如OMCS網(wǎng)絡(luò)語(yǔ)音視頻框架,它實(shí)現(xiàn)了多媒體設(shè)備(麥克風(fēng)宦焦、攝像頭发钝、桌面、電子白板)的采集波闹、編碼酝豪、網(wǎng)絡(luò)傳送、解碼精堕、播放(或顯示)等相關(guān)的一整套流程孵淘,可以快速地開(kāi)發(fā)出視頻聊天系統(tǒng)、視頻會(huì)議系統(tǒng)歹篓、遠(yuǎn)程醫(yī)療系統(tǒng)瘫证、遠(yuǎn)程教育系統(tǒng)、網(wǎng)絡(luò)監(jiān)控系統(tǒng)等等基于網(wǎng)絡(luò)多媒體的應(yīng)用系統(tǒng)庄撮。然而痛悯,OMCS直接支持的是通用的語(yǔ)音視頻設(shè)備,而在某些系統(tǒng)中重窟,需要使用網(wǎng)絡(luò)攝像頭或者特殊的視頻采集卡作為視頻源,或者其它的聲音采集設(shè)備作為音頻源惧财,OMCS則提供了擴(kuò)展接口——用戶自己實(shí)現(xiàn)這個(gè)擴(kuò)展的接口巡扇,然后以“依賴注入”的方式將對(duì)象實(shí)例注入到OMCS中,從而完成對(duì)音垮衷、視頻設(shè)備的擴(kuò)展厅翔。
“依賴注入”常常用于擴(kuò)展,尤其是在開(kāi)發(fā)框架的設(shè)計(jì)中搀突。從某種意義上來(lái)說(shuō)刀闷,任何開(kāi)發(fā)框架,天生都是不完整的應(yīng)用程序仰迁。因此甸昏,一個(gè)優(yōu)秀的開(kāi)發(fā)框架,不僅要讓開(kāi)發(fā)者能夠重用這些久經(jīng)考驗(yàn)的的卓越的解決方案徐许,也要讓開(kāi)發(fā)者能夠向框架中插入自定義的業(yè)務(wù)邏輯施蜜,從而靈活自由地適應(yīng)特定的業(yè)務(wù)場(chǎng)景的需要——也就是說(shuō)要具備良好的可擴(kuò)展性。比如上面提到的OMCS網(wǎng)絡(luò)語(yǔ)音視頻框架可應(yīng)用于音雌隅、視頻聊天系統(tǒng)翻默、視頻會(huì)議系統(tǒng)缸沃、遠(yuǎn)程醫(yī)療系統(tǒng)、遠(yuǎn)程教育系統(tǒng)修械、網(wǎng)絡(luò)監(jiān)控系統(tǒng)等等基于網(wǎng)絡(luò)多媒體的應(yīng)用系統(tǒng)趾牧;以及ESFramework通信框架能夠應(yīng)用于即時(shí)通訊系統(tǒng),大型多人在線游戲肯污、在線網(wǎng)頁(yè)游戲翘单、文件傳送系統(tǒng)、數(shù)據(jù)采集系統(tǒng)仇箱、分布式OA系統(tǒng)等任何需要分布式通信的軟件系統(tǒng)中——這種良好的擴(kuò)展性都與“依賴注入”的使用密不可分县恕!
·面向接口編程
談到最后,“面向接口編程”已經(jīng)是呼之欲出剂桥。無(wú)論是依賴倒置忠烛、控制反轉(zhuǎn)、還是依賴注入权逗,都已經(jīng)蘊(yùn)含著“面向接口編程”的思想美尸。面向接口,就意味著面向抽象斟薇。作為哲學(xué)范疇而言师坎,規(guī)定性少稱為抽象,規(guī)定性多稱為具體堪滨。而接口胯陋,就是程序中的一種典型的“抽象”的形式。面向抽象袱箱,就意味著面向事物的本質(zhì)規(guī)定性遏乔,擺脫感性雜多的牽絆,從而把握住“必然”——而這本身就意味著自由发笔,因?yàn)樽杂删褪菍?duì)必然的認(rèn)識(shí)盟萨。
也許以上的這段論述太過(guò)“哲學(xué)”,但是“一本之理”與“萬(wàn)殊之理”本身就“體用不二”——總結(jié)來(lái)看了讨,依賴倒置捻激、控制反轉(zhuǎn)、依賴注入都圍繞著“解耦和”的問(wèn)題前计,而同時(shí)自始至終又都是“面向接口編程”的方法——因此胞谭,“面向接口編程”天生就是“解耦和”的好辦法。由此也印證了從“抽象”到“自由”的這一段范疇的辯證衍化男杈。
“面向?qū)ο蟆迸c“面向接口”并非兩種不同的方法學(xué)韭赘,“面向接口”其實(shí)是“面向?qū)ο蟆钡膬?nèi)在要求,是其一部分內(nèi)涵的集中表述势就。我們對(duì)于理想軟件的期待常被概括為“高內(nèi)聚泉瞻,低耦合”脉漏,這也是整個(gè)現(xiàn)代軟件開(kāi)發(fā)方法學(xué)所追求的目標(biāo)。面向?qū)ο蠓椒▽W(xué)作為現(xiàn)代軟件開(kāi)發(fā)方法學(xué)的代表袖牙,本身就蘊(yùn)含著“高內(nèi)聚侧巨,低耦合”的思想精髓,從這個(gè)意義上來(lái)說(shuō)鞭达,“面向?qū)ο蟆边@個(gè)表述更加側(cè)重于“高內(nèi)聚”司忱,“面向接口”的表述則更加側(cè)重于“低耦合”——不過(guò)是同一事物的不同側(cè)面罷了。
除此之外畴蹭,我們也能從“面向接口編程”的思想中得到“世俗”的啟迪——《論語(yǔ)》里面講坦仍,不患無(wú)位,患所以立叨襟;不患人之不己知繁扎,患其不能也——就是教導(dǎo)我們要面向“我有沒(méi)有的本事?”糊闽、“我有沒(méi)有能力梳玫?”這樣的接口,而不是面向“我有沒(méi)有搞到位子右犹?”提澎、“別人了不了解我?”這樣的具體念链。依我看盼忌,這是莫大的教誨!
2.2 六大原則詳細(xì)講解
- 單一原則
【】定義:
不要存在多于一個(gè)導(dǎo)致類變更的原因掂墓。通俗的說(shuō)谦纱,即一個(gè)類只負(fù)責(zé)一項(xiàng)職責(zé)。
【】問(wèn)題由來(lái):
類T負(fù)責(zé)兩個(gè)不同的職責(zé):職責(zé)P1梆暮,職責(zé)P2。當(dāng)由于職責(zé)P1需求發(fā)生改變而需要修改類T時(shí)绍昂,有可能會(huì)導(dǎo)致原本運(yùn)行正常的職責(zé)P2功能發(fā)生故障啦粹。 - 依賴倒置原則
就是盡量依賴接口,不要依賴具體實(shí)現(xiàn)窘游,依賴一個(gè)很少變化的對(duì)象唠椭,如果你依賴的實(shí)現(xiàn)對(duì)象老是變化,那你豈不是也要跟著老是改動(dòng)忍饰,這樣很不爽贪嫂,對(duì)吧!
【】定義:
高層模塊不應(yīng)該依賴低層模塊艾蓝,二者都應(yīng)該依賴其抽象力崇;抽象不應(yīng)該依賴細(xì)節(jié)斗塘;細(xì)節(jié)應(yīng)該依賴抽象。
【】問(wèn)題由來(lái):
類A直接依賴類B亮靴,假如要將類A改為依賴類C馍盟,則必須通過(guò)修改類A的代碼來(lái)達(dá)成。這種場(chǎng)景下茧吊,類A一般是高層模塊贞岭,負(fù)責(zé)復(fù)雜的業(yè)務(wù)邏輯;類B和類C是低層模塊搓侄,負(fù)責(zé)基本的原子操作瞄桨;假如修改類A,會(huì)給程序帶來(lái)不必要的風(fēng)險(xiǎn)讶踪。
【】解決方案:
將類A修改為依賴接口I芯侥,類B和類C各自實(shí)現(xiàn)接口I,類A通過(guò)接口I間接與類B或者類C發(fā)生聯(lián)系俊柔,則會(huì)大大降低修改類A的幾率筹麸。
依賴倒置原則基于這樣一個(gè)事實(shí):相對(duì)于細(xì)節(jié)的多變性词身,抽象的東西要穩(wěn)定的多忙干。以抽象為基礎(chǔ)搭建起來(lái)的架構(gòu)比以細(xì)節(jié)為基礎(chǔ)搭建起來(lái)的架構(gòu)要穩(wěn)定的多。在java中较坛,抽象指的是接口或者抽象類留晚,細(xì)節(jié)就是具體的實(shí)現(xiàn)類酵紫,使用接口或者抽象類的目的是制定好規(guī)范和契約,而不去涉及任何具體的操作错维,把展現(xiàn)細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類去完成奖地。
依賴倒置原則的核心思想是面向接口編程,我們依舊用一個(gè)例子來(lái)說(shuō)明面向接口編程比相對(duì)于面向?qū)崿F(xiàn)編程好在什么地方赋焕。場(chǎng)景是這樣的参歹,母親給孩子講故事,只要給她一本書隆判,她就可以照著書給孩子講故事了 - 接口隔離原則
【】定義:
客戶端不應(yīng)該依賴它不需要的接口犬庇;一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上。
【】問(wèn)題由來(lái):
類A通過(guò)接口I依賴類B侨嘀,類C通過(guò)接口I依賴類D臭挽,如果接口I對(duì)于類A和類B來(lái)說(shuō)不是最小接口,則類B和類D必須去實(shí)現(xiàn)他們不需要的方法咬腕。
通過(guò)這兩個(gè)圖欢峰,可以很容易發(fā)現(xiàn),最小接口原則,就是提取公因數(shù)到一個(gè)接口里邊纽帖,向上面的I1宠漩,方法1都提取到了這個(gè)接口中
- 里式替換原則
肯定有不少人跟我剛看到這項(xiàng)原則的時(shí)候一樣,對(duì)這個(gè)原則的名字充滿疑惑抛计。其實(shí)原因就是這項(xiàng)原則最早是在1988年哄孤,由麻省理工學(xué)院的一位姓里的女士(Barbara Liskov)提出來(lái)的。
【】定義1:
如果對(duì)每一個(gè)類型為 T1的對(duì)象 o1吹截,都有類型為 T2 的對(duì)象o2瘦陈,使得以 T1定義的所有程序 P 在所有的對(duì)象 o1 都代換成 o2 時(shí),程序 P 的行為沒(méi)有發(fā)生變化波俄,那么類型 T2 是類型 T1 的子類型晨逝。
【】定義2:
所有引用基類的地方必須能透明地使用其子類的對(duì)象。
【】問(wèn)題由來(lái):
有一功能P1懦铺,由類A完成∽矫玻現(xiàn)需要將功能P1進(jìn)行擴(kuò)展,擴(kuò)展后的功能為P冬念,其中P由原有功能P1與新功能P2組成趁窃。新功能P由類A的子類B來(lái)完成,則子類B在完成新功能P2的同時(shí)急前,有可能會(huì)導(dǎo)致原有功能P1發(fā)生故障醒陆。
【】解決方案:
當(dāng)使用繼承時(shí),遵循里氏替換原則裆针。類B繼承類A時(shí)刨摩,除添加新的方法完成新增功能P2外,盡量不要重寫父類A的方法世吨,也盡量不要重載父類A的方法澡刹。
繼承包含這樣一層含義:
父類中凡是已經(jīng)實(shí)現(xiàn)好的方法(相對(duì)于抽象方法而言),實(shí)際上是在設(shè)定一系列的規(guī)范和契約耘婚,雖然它不強(qiáng)制要求所有的子類必須遵從這些契約罢浇,但是如果子類對(duì)這些非抽象方法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞沐祷。而里氏替換原則就是表達(dá)了這一層含義嚷闭。
繼承作為面向?qū)ο笕筇匦灾唬诮o程序設(shè)計(jì)帶來(lái)巨大便利的同時(shí)戈轿,也帶來(lái)了弊端凌受。比如使用繼承會(huì)給程序帶來(lái)侵入性阵子,程序的可移植性降低思杯,增加了對(duì)象間的耦合性,如果一個(gè)類被其他的類所繼承,則當(dāng)這個(gè)類需要修改時(shí)色乾,必須考慮到所有的子類誊册,并且父類修改后,所有涉及到子類的功能都有可能會(huì)產(chǎn)生故障暖璧。 - 迪米特原則
【】定義:
一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最少的了解案怯。
【】問(wèn)題由來(lái):
類與類之間的關(guān)系越密切,耦合度越大澎办,當(dāng)一個(gè)類發(fā)生改變時(shí)嘲碱,對(duì)另一個(gè)類的影響也越大。
【】解決方案:
盡量降低類與類之間的耦合局蚀。
【】簡(jiǎn)介:
迪米特法則又叫最少知道原則麦锯,最早是在1987年由美國(guó)Northeastern University的Ian Holland提出。通俗的來(lái)講琅绅,就是一個(gè)類對(duì)自己依賴的類知道的越少越好扶欣。也就是說(shuō),對(duì)于被依賴的類來(lái)說(shuō)千扶,無(wú)論邏輯多么復(fù)雜料祠,都盡量地的將邏輯封裝在類的內(nèi)部,對(duì)外除了提供的public方法澎羞,不對(duì)外泄漏任何信息髓绽。迪米特法則還有一個(gè)更簡(jiǎn)單的定義:只與直接的朋友通信。首先來(lái)解釋一下什么是直接的朋友:每個(gè)對(duì)象都會(huì)與其他對(duì)象有耦合關(guān)系煤痕,只要兩個(gè)對(duì)象之間有耦合關(guān)系梧宫,我們就說(shuō)這兩個(gè)對(duì)象之間是朋友關(guān)系。耦合的方式很多摆碉,依賴塘匣、關(guān)聯(lián)、組合巷帝、聚合等忌卤。其中,我們稱出現(xiàn)成員變量楞泼、方法參數(shù)驰徊、方法返回值中的類為直接的朋友,而出現(xiàn)在局部變量中的類則不是直接的朋友堕阔。也就是說(shuō)棍厂,陌生的類最好不要作為局部變量的形式出現(xiàn)在類的內(nèi)部。
【】看下面這個(gè)例子
舉一個(gè)例子:有一個(gè)集團(tuán)公司超陆,下屬單位有分公司和直屬部門牺弹,現(xiàn)在要求打印出所有下屬單位的員工ID浦马。先來(lái)看一下違反迪米特法則的設(shè)計(jì)。
//總公司員工
class Employee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}
//分公司員工
class SubEmployee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;f
}
}
class SubCompanyManager{
public List<SubEmployee> getAllEmployee(){
List<SubEmployee> list = new ArrayList<SubEmployee>();
for(int i=0; i<100; i++){
SubEmployee emp = new SubEmployee();
//為分公司人員按順序分配一個(gè)ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
}
class CompanyManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//為總公司人員按順序分配一個(gè)ID
emp.setId("總公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
List<SubEmployee> list1 = sub.getAllEmployee();
for(SubEmployee e:list1){
System.out.println(e.getId());
}
List<Employee> list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
public class Client{
public static void main(String[] args){
CompanyManager e = new CompanyManager();
e.printAllEmployee(new SubCompanyManager());
}
}
現(xiàn)在這個(gè)設(shè)計(jì)的主要問(wèn)題出在CompanyManager中张漂,根據(jù)迪米特法則晶默,只與直接的朋友發(fā)生通信,而SubEmployee類并不是CompanyManager類的直接朋友(以局部變量出現(xiàn)的耦合不屬于直接朋友)航攒,從邏輯上講總公司只與他的分公司耦合就行了磺陡,與分公司的員工并沒(méi)有任何聯(lián)系,這樣設(shè)計(jì)顯然是增加了不必要的耦合漠畜。按照迪米特法則币他,應(yīng)該避免類中出現(xiàn)這樣非直接朋友關(guān)系的耦合。修改后的代碼如下:
class SubCompanyManager{
public List<SubEmployee> getAllEmployee(){
List<SubEmployee> list = new ArrayList<SubEmployee>();
for(int i=0; i<100; i++){
SubEmployee emp = new SubEmployee();
//為分公司人員按順序分配一個(gè)ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
public void printEmployee(){
List<SubEmployee> list = this.getAllEmployee();
for(SubEmployee e:list){
System.out.println(e.getId());
}
}
}
class CompanyManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//為總公司人員按順序分配一個(gè)ID
emp.setId("總公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
sub.printEmployee();
List<Employee> list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
這兩個(gè)代碼的區(qū)別憔狞,就是
避免了總公司的打印方法操作子公司的員工信息躯喇,這就是所謂的不要和非直接朋友關(guān)系的類進(jìn)行通信辫封,
- 開(kāi)閉原則-沒(méi)有實(shí)際操作層面的原則,前面的原則執(zhí)行好了自然就開(kāi)閉了
【】定義:
一個(gè)軟件實(shí)體如類廉丽、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放倦微,對(duì)修改關(guān)閉。
【】問(wèn)題由來(lái):
在軟件的生命周期內(nèi)正压,因?yàn)樽兓栏!⑸?jí)和維護(hù)等原因需要對(duì)軟件原有代碼進(jìn)行修改時(shí),可能會(huì)給舊代碼中引入錯(cuò)誤焦履,也可能會(huì)使我們不得不對(duì)整個(gè)功能進(jìn)行重構(gòu)拓劝,并且需要原有代碼經(jīng)過(guò)重新測(cè)試
【】解決方案:
當(dāng)軟件需要變化時(shí),盡量通過(guò)擴(kuò)展軟件實(shí)體的行為來(lái)實(shí)現(xiàn)變化嘉裤,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)變化
【】理解:
開(kāi)閉原則是面向?qū)ο笤O(shè)計(jì)中最基礎(chǔ)的設(shè)計(jì)原則郑临,它指導(dǎo)我們?nèi)绾谓⒎€(wěn)定靈活的系統(tǒng)。開(kāi)閉原則可能是設(shè)計(jì)模式六項(xiàng)原則中定義最模糊的一個(gè)了屑宠,它只告訴我們對(duì)擴(kuò)展開(kāi)放厢洞,對(duì)修改關(guān)閉,可是到底如何才能做到對(duì)擴(kuò)展開(kāi)放典奉,對(duì)修改關(guān)閉躺翻,并沒(méi)有明確的告訴我們。以前卫玖,如果有人告訴我“你進(jìn)行設(shè)計(jì)的時(shí)候一定要遵守開(kāi)閉原則”公你,我會(huì)覺(jué)的他什么都沒(méi)說(shuō),但貌似又什么都說(shuō)了假瞬。因?yàn)殚_(kāi)閉原則真的太虛了陕靠。
三 常見(jiàn)設(shè)計(jì)模式
把uml類圖和設(shè)計(jì)模式的基本概念放在前面講嚣崭,是因?yàn)橄旅婧芏喔拍畹念悎D說(shuō)明會(huì)用到uml類圖知識(shí)和設(shè)計(jì)模式知識(shí),在學(xué)習(xí)六大原則和類圖的過(guò)程中懦傍,也要一并將上面的知識(shí)進(jìn)行融合消化理解,學(xué)習(xí)每個(gè)設(shè)計(jì)模式芦劣,盡量都要理解在uml類圖解釋他們的說(shuō)明圖和設(shè)計(jì)模式原則層面的知識(shí)粗俱!
我們很多偉大工作者為了將知識(shí)更方便的傳播,將知識(shí)進(jìn)行了分類傳播虚吟,但是知識(shí)接收的過(guò)程中寸认,如果要進(jìn)行消化吸收,也必須獲取到像知識(shí)傳播者一樣足夠多的信息進(jìn)行知識(shí)融合串慰,才能夠還原作者的意圖偏塞,所以知識(shí)學(xué)習(xí)要逆?zhèn)鞑ミ^(guò)程,這個(gè)很像我們的osi 7層模型一樣邦鲫。
3.1 創(chuàng)建型模式
前面講過(guò)灸叼,社會(huì)化的分工越來(lái)越細(xì),自然在軟件設(shè)計(jì)方面也是如此庆捺,因此對(duì)象的創(chuàng)建和對(duì)象的使用分開(kāi)也就成為了必然趨勢(shì)古今。因?yàn)閷?duì)象的創(chuàng)建會(huì)消耗掉系統(tǒng)的很多資源,所以單獨(dú)對(duì)對(duì)象的創(chuàng)建進(jìn)行研究滔以,從而能夠高效地創(chuàng)建對(duì)象就是創(chuàng)建型模式要探討的問(wèn)題捉腥。這里有6個(gè)具體的創(chuàng)建型模式可供研究,它們分別是:
簡(jiǎn)單工廠模式(Simple Factory)
工廠方法模式(Factory Method)
抽象工廠模式(Abstract Factory)
創(chuàng)建者模式(Builder)
原型模式(Prototype)
單例模式(Singleton)
說(shuō)明:嚴(yán)格來(lái)說(shuō)你画,簡(jiǎn)單工廠模式不是GoF總結(jié)出來(lái)的23種設(shè)計(jì)模式之一
- 工廠模式
工廠方法模式 每個(gè)產(chǎn)品需要一個(gè)工廠子類抵碟,工廠子類太多。
抽象工廠坏匪,最大特點(diǎn)就是拟逮,產(chǎn)品有多個(gè)等級(jí),每個(gè)等級(jí)有多個(gè)規(guī)格和參數(shù)