·內(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)成立巍膘,必須要這樣寫(xiě):
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">
public
bool
AdmireFlowers()
{
return
flower .IsBloomed; ;
}
</pre>
無(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è)正向依賴的例子:
案疲。tcpServerEngine是StriveEngine.dll提供通信引擎封恰,它發(fā)布有一個(gè)MessageReceived事件。假設(shè)我定義了一個(gè)CustomizeHandler類來(lái)用于消息處理褐啡,那么CustomizeHandler的內(nèi)部需要預(yù)定tcpServerEngine的MessageReceived事件诺舔,因此customizeHandler依賴于tcpServerEngine,這就是一個(gè)普通的依賴關(guān)系备畦,也就是高層模塊依賴于低層模塊低飒。

而
[ESFramework
通信框架](http://www.cnblogs.com/zhuweisky/archive/2010/08/12/1798211.html)
則應(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)涵鹤竭。
首先我們要區(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ù)方,而我們自己編寫(xiě)的業(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)屬于Client,而一旦控制權(quán)交到server刀荒,就叫控制反轉(zhuǎn)代嗤。比如你去下館子,你是Client餐館是server缠借。你點(diǎn)菜干毅,餐館負(fù)責(zé)做菜,程序流程的控制權(quán)屬于Client烈炭;而如果你去自助餐廳溶锭,程序流程的控制權(quán)就轉(zhuǎn)到server了,也就是控制反轉(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
通信框架](http://www.cnblogs.com/zhuweisky/archive/2010/08/12/1798211.html) 的例子,通信引擎回調(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](http://blog.oraycn.com/StriveEngine.aspx) 吧恃,通過(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
)
依賴注入與依賴倒置任内、控制反轉(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)看莺褒。
[圖片上傳中...(image-e310f6-1511840754597-0)]
比如
掩缓,它實(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ò)展方法
詳情參考](http://www.cnblogs.com/zhuweisky/p/3651229.html)
“依賴注入”常常用于擴(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);以及
能夠應(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)有搞到位子?”呆躲、“別人了不了解我异逐?”這樣的具體。依我看插掂,這是莫大的教誨灰瞻!