[2]動(dòng)機(jī)、原則與模式——OO設(shè)計(jì)之我見(jiàn)

OO設(shè)計(jì)的三寶
在講具體的原則之前匣吊,我想先明確一下面向?qū)ο笳Z(yǔ)言的三個(gè)特性儒拂。所有的面向?qū)ο笳Z(yǔ)言都首先必須支持這三個(gè)特性,才能稱之為OO語(yǔ)言色鸳。也只有這三個(gè)特性的支持社痛,才有了后面的各種原則和模式之說(shuō)。所以這是OO設(shè)計(jì)的“三寶”命雀。

首先是封裝蒜哀,封裝的本質(zhì)的是將行為寓于數(shù)據(jù)之中。注意到這是面向?qū)ο笳Z(yǔ)言與面向過(guò)程語(yǔ)言最大的區(qū)別吏砂,不用贅述撵儿。其次是繼承乘客,繼承對(duì)于老的教學(xué)方式,總是強(qiáng)調(diào)拓展屬性淀歇,即具體到更具體寨典。這是欠妥當(dāng)?shù)摹O鄳?yīng)的房匆,我們應(yīng)該更多的認(rèn)為繼承是一種橋梁耸成,把抽象和具體連接起來(lái)。

多態(tài)是三寶中最重要的一個(gè)浴鸿。封裝和繼承都是為了多態(tài)做鋪墊井氢。正因?yàn)橛辛硕鄳B(tài),才有了面向?qū)ο笤O(shè)計(jì)的那些原則和模式岳链,才有可能產(chǎn)生高內(nèi)聚低耦合的軟件系統(tǒng)花竞。所以說(shuō),對(duì)于軟件開(kāi)發(fā)掸哑,多態(tài)就像是普羅米修斯帶給人類的圣火约急。這種評(píng)價(jià)是毫不夸張的,越懂OO的設(shè)計(jì)苗分,越能理解多態(tài)的重要性厌蔽。Java成為OO語(yǔ)言的翹楚,我個(gè)人認(rèn)為與其天然的支持多態(tài)是有一定關(guān)系的摔癣。

我用一個(gè)形象的例子總結(jié)一下OO語(yǔ)言的這三個(gè)特性奴饮。假設(shè)我們有一個(gè)異質(zhì)鏈表,類型為OfficeTool择浊,這個(gè)抽象類對(duì)象代表一種Office工具戴卜。它會(huì)有很多的方法,例如有一個(gè)方法叫g(shù)etYourBestOutput琢岩,意即“返回自己最好的輸出”投剥。(方法寓于對(duì)象之中,這就是封裝担孔。)這個(gè)鏈表中有不同的對(duì)象江锨,它們都是OfficeTool這種對(duì)象的子類,其中三個(gè)就是Word攒磨,Excel和PowerPoint泳桦。(子對(duì)象擁有父對(duì)象的方法,可以以父對(duì)象的名字進(jìn)行引用娩缰,這就是繼承。)如果遍歷這個(gè)異質(zhì)鏈表谒府,訪問(wèn)剛才提到的方法時(shí)拼坎,我們知道浮毯,這三個(gè)工具各有所長(zhǎng),所以Word會(huì)輸出一部精心排版的書稿泰鸡,Excel會(huì)輸出一份內(nèi)容詳實(shí)的財(cái)報(bào)债蓝,而PowerPoint會(huì)輸出一個(gè)制作精美的演示文稿。(不同子類的相同方法盛龄,表現(xiàn)出不同的結(jié)果或輸出饰迹,這就是多態(tài)!)

迪米特法則
迪米特法則有多種表述:

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.(每一個(gè)軟件單位對(duì)其他的單位都只有最少的知識(shí)余舶,而且局限于那些與本單位密切相關(guān)的軟件單位啊鸭。)
Each unit should only talk to its friends; don't talk to strangers.(每個(gè)軟件單位都只與自己的朋友對(duì)話,不要和陌生人說(shuō)話匿值。)
Only talk to your immediate friends.(只與你最親近的朋友通信赠制。)

作為法律,法則挟憔,它強(qiáng)調(diào)了一種對(duì)軟件系統(tǒng)普世的原則钟些,即“高內(nèi)聚,低耦合”绊谭。盡量減少通信政恍,保持內(nèi)部高度統(tǒng)一。實(shí)際上你去網(wǎng)上搜达传,該法則也不是完美的抚垃,但是它傳遞了一種思想,我認(rèn)為OO設(shè)計(jì)原則的源頭在此趟大。因?yàn)槟阋裱厦滋胤▌t鹤树,你就要考慮整個(gè)軟件系統(tǒng)哪些元素應(yīng)該聚合在一起,能夠產(chǎn)生什么行為才是高內(nèi)聚的逊朽,如何進(jìn)行交互才是低耦合的罕伯。這本身不就是設(shè)計(jì)的過(guò)程么?而且我認(rèn)為如果能考慮這些問(wèn)題叽讳,這還很有可能是一個(gè)優(yōu)秀的設(shè)計(jì)追他。

S.O.L.I.D.原則
Robert Martin有一本非常著名的書,《敏捷軟件開(kāi)發(fā):原則岛蚤、模式與實(shí)踐》邑狸。他在這里提到SOLID是最初的五個(gè)原則,我感覺(jué)這就像是說(shuō)亞當(dāng)和夏娃是最初的2個(gè)人一樣涤妒。其實(shí)還是強(qiáng)調(diào)原則重于模式单雾。我下面會(huì)談?wù)剢我宦氊?zé)和接口隔離,因?yàn)樗鼈冇幸欢ǖ南嗨菩裕踩菀渍莆展瓒选:笕齻€(gè)是OO設(shè)計(jì)的精髓屿储,體現(xiàn)了延遲實(shí)現(xiàn)和針對(duì)接口編程的核心思想。

單一職責(zé)原則
Every context (class, function, variable, etc.) should have a single responsibility, and that responsibility should be entirely encapsulated by the context.(每個(gè)實(shí)體都應(yīng)該只有一種職責(zé)渐逃,且這種職責(zé)被完全的包裹在該實(shí)體內(nèi)够掠。)

這個(gè)原則相對(duì)簡(jiǎn)單,只要你多想想是不是把2個(gè)以上無(wú)關(guān)的事情放到了一個(gè)單位里茄菊,就可以避免過(guò)大而冗余的類疯潭。記住,10000行代碼的類面殖,不是你的榮譽(yù)竖哩,而是你的恥辱。如果10000行的類需要復(fù)用畜普,請(qǐng)問(wèn)有復(fù)用的可能和切實(shí)可行的辦法么期丰?就一個(gè)類而言,應(yīng)該只有一個(gè)引起它變化的原因吃挑。我們經(jīng)常會(huì)遇到User一改需求钝荡,就要改同一個(gè)類,即使需求之間沒(méi)多大關(guān)聯(lián)舶衬,這就說(shuō)明我們違背了單一職責(zé)原則埠通,賦予了一個(gè)類太多的職責(zé)。

接口隔離原則
Once an interface has become too 'fat' it needs to be split into smaller and more specific interfaces so that any clients of the interface will only know about the methods that pertain to them.(一旦一個(gè)接口過(guò)于“臃腫”逛犹,需要把它拆分成更小和更專一的接口端辱,為的是實(shí)現(xiàn)接口的類,只需要知道和自己相關(guān)的方法虽画。)

對(duì)接口的設(shè)計(jì)同樣要遵循迪米特法則舞蔽。一旦一個(gè)接口過(guò)于“臃腫”,需要把它拆分成更小和更專一的接口码撰,為的是實(shí)現(xiàn)接口的類渗柿,只需要知道和自己相關(guān)的方法。最好的例子就是Java中的一些接口定義脖岛。比如Java類庫(kù)中提供的Comparable和Serializable接口朵栖。如果你通過(guò)compare方法給出了實(shí)現(xiàn)Comparable接口的類的兩個(gè)對(duì)象的比較結(jié)果,一個(gè)int值柴梆。你就可以在一些排序的數(shù)據(jù)結(jié)構(gòu)中很好的承載這些對(duì)象陨溅,達(dá)到你比較他們的目的,比TreeTable绍在;Serializable做法更絕门扇,是一個(gè)沒(méi)有方法的接口雹有,相當(dāng)于僅僅是一個(gè)帽子,是一個(gè)標(biāo)記悯嗓,說(shuō)明只要繼承這個(gè)接口的類才能被序列化件舵,否則就拋出異常卸察。

開(kāi)/閉原則
Software entities should be open for extension, but closed for modification.(軟件實(shí)體應(yīng)該只做擴(kuò)展脯厨,而不做修改。)

開(kāi)閉原則是最簡(jiǎn)單的但很難做到的坑质。繼承應(yīng)當(dāng)被看做是封裝變化的方法合武,而不應(yīng)當(dāng)被認(rèn)為是從一般的對(duì)象生成特殊的對(duì)象的方法。這是《Java與模式》那本書作者的原話涡扼。對(duì)于它的解讀是稼跳,完美的繼承是從抽象類到具體類的過(guò)程。即具體類通過(guò)繼承抽象類而封裝了不同的方法(方法接口在抽象類說(shuō)明)吃沪。錯(cuò)誤的繼承是在一般的對(duì)象基礎(chǔ)上汤善,通過(guò)加入特殊的方法,而形成特殊的對(duì)象票彪。

抽象化是開(kāi)/閉原則的關(guān)鍵红淡。在Java、C#等編程語(yǔ)言中降铸,可以為系統(tǒng)定義一個(gè)相對(duì)穩(wěn)定的抽象層在旱,而將不同的實(shí)現(xiàn)行為移至具體的實(shí)現(xiàn)層中完成。這是第一次提出行為的延后實(shí)現(xiàn)推掸,稍后會(huì)看到這個(gè)動(dòng)作的最后落腳點(diǎn)桶蝎。

里氏替換原則
In a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).(如果對(duì)每一個(gè)類型為T1的對(duì)象o1,都有類型為T2的對(duì)象o2谅畅,使得以T1定義的所有程序P在所有的對(duì)象o1都代換成o2時(shí)登渣,程序P的行為沒(méi)有變化,那么類型T2是類型T1的子類型毡泻。)

里氏替換原則是實(shí)現(xiàn)開(kāi)/閉原則的重要方式之一胜茧,由于使用基類對(duì)象的地方都可以使用子類對(duì)象,因此在程序中盡量使用基類類型來(lái)對(duì)對(duì)象進(jìn)行定義牙捉,而在運(yùn)行時(shí)再確定其子類類型竹揍,用子類對(duì)象來(lái)替換父類對(duì)象。

依賴反轉(zhuǎn)原則
Abstractions should not depend upon details. Details should depend upon abstractions. Program to an interface, not an implementation.(抽象不應(yīng)該依賴于具體邪铲,具體應(yīng)該依賴于抽象芬位。)

由于有了開(kāi)/閉原則和里氏替換原則的鋪墊,這里提出了最核心的原則带到,針對(duì)接口編程昧碉,延緩細(xì)節(jié)的實(shí)現(xiàn)。如果說(shuō)開(kāi)/閉原則是目標(biāo),里氏替換原則是行為保證被饿,那么按接口編程的依賴反轉(zhuǎn)原則就是有理論保證的四康,有實(shí)際目標(biāo)的,真正的高質(zhì)量OO設(shè)計(jì)狭握。其實(shí)有人已經(jīng)把該原則叫做OO設(shè)計(jì)的標(biāo)志闪金,足見(jiàn)其重要性。

這三個(gè)重要的OO原則和三個(gè)OO語(yǔ)言特性的本質(zhì)關(guān)系是這樣的:開(kāi)/閉原則要求我們盡量在構(gòu)造軟件實(shí)體的時(shí)候论颅,應(yīng)該使用擴(kuò)展哎垦,而不是修改原來(lái)的對(duì)象;那么繼承是一個(gè)很好的方式恃疯,繼承在理想化的使用場(chǎng)景中漏设,應(yīng)該是從抽象到具體(將行為封裝到一個(gè)具體對(duì)象之中),而不是從一種具體到另外一種具體今妄。為什么郑口?因?yàn)槔锸咸鎿Q原則要求行為一致,才是繼承的關(guān)系盾鳞。這和我們理解的加一個(gè)extends就定義了子類和父類的關(guān)系是不同的犬性。由于抽象類沒(méi)有具體行為的實(shí)現(xiàn),所以對(duì)抽象類的繼承雁仲,天然的是符合里氏替換原則的真正的繼承仔夺。而具體到具體的繼承,很難保證里氏替換原則的實(shí)現(xiàn)缸兔。

在滿足開(kāi)/閉原則和里氏替換原則的基礎(chǔ)上吹艇,對(duì)同一個(gè)抽象類的行為,不同的實(shí)現(xiàn)了繼承的具體類表現(xiàn)了不同的行為受神,這就是多態(tài)〔浦回想到前面我們提到的Office異質(zhì)鏈表的例子撑碴,我們的遍歷操作,是針對(duì)抽象類的行為來(lái)進(jìn)行編程的醉拓,這就是針對(duì)“接口/抽象類”編程的意義收苏,這也是編程從依賴具體類(Word愤兵,Excel和PowerPoint)倒轉(zhuǎn)為依賴Office這個(gè)抽象類的過(guò)程。依賴倒轉(zhuǎn)原則的精髓就在于此懦鼠。

再論重構(gòu)
最后我想再絮叨兩句重構(gòu)的話題矫夷。重構(gòu)來(lái)源于那本著名的書憋槐。那些“壞味道”,也隨重構(gòu)的概念被程序員所熟知阳仔。但如果掌握了以上所說(shuō)的OO設(shè)計(jì)的原則并應(yīng)用于設(shè)計(jì)和實(shí)現(xiàn)階段,那么有些“壞味道”根本就不會(huì)發(fā)生嘶摊,那么重構(gòu)也不會(huì)發(fā)生了评矩。我把重構(gòu)分為2種,一種是簡(jiǎn)單的重構(gòu)斥杜,就像修改文章中的改正錯(cuò)別字,或者調(diào)整個(gè)別語(yǔ)句的順序蔗喂;另一種是結(jié)構(gòu)上的重構(gòu),是由于業(yè)務(wù)邏輯的變化或完善畦粮,導(dǎo)致設(shè)計(jì)方案的進(jìn)化(注意我并沒(méi)有說(shuō)完全推翻)乖阵,這時(shí)的重構(gòu)才是最有價(jià)值的。一個(gè)掌握了OO設(shè)計(jì)精髓和原則的程序員瞪浸,應(yīng)該著眼于結(jié)構(gòu)上的重構(gòu),而在正常的編碼中就要注意避免默终,數(shù)百行的函數(shù)犁罩,隨意定義變量和分配內(nèi)存两疚,大量的重復(fù)代碼等問(wèn)題。賈島有時(shí)間在“推”和“敲”上反復(fù)斟酌丐巫,而曹植只有七步的時(shí)間醞釀自己的詩(shī)篇勺美,講究的就是一氣呵成。程序員的能力和效率往往就體現(xiàn)在這些不經(jīng)意的地方赡茸。所以學(xué)無(wú)止境,以此共勉吧遗菠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辙纬,隨后出現(xiàn)的幾起案子叭喜,更是在濱河造成了極大的恐慌贺拣,老刑警劉巖譬涡,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件启绰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡委可,警方通過(guò)查閱死者的電腦和手機(jī)着倾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卡者,“玉大人,你說(shuō)我怎么就攤上這事材诽。” “怎么了脸侥?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)官边。 經(jīng)常有香客問(wèn)我外遇,道長(zhǎng),這世上最難降的妖魔是什么跳仿? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮玩徊,結(jié)果婚禮上谨究,老公的妹妹穿的比我還像新娘。我一直安慰自己泣棋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布鸯屿。 她就那樣靜靜地躺著把敢,像睡著了一般寄摆。 火紅的嫁衣襯著肌膚如雪修赞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天勾邦,我揣著相機(jī)與錄音割择,去河邊找鬼眷篇。 笑死荔泳,一個(gè)胖子當(dāng)著我的面吹牛虐杯,可吹牛的內(nèi)容都是我干的昧港。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼慨飘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼确憨!你這毒婦竟也來(lái)了瓤的?” 一聲冷哼從身側(cè)響起圈膏,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稽坤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尿褪,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摆马。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臼闻。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖述呐,靈堂內(nèi)的尸體忽然破棺而出蕉毯,到底是詐尸還是另有隱情乓搬,我是刑警寧澤恕刘,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布褐着,位于F島的核電站,受9級(jí)特大地震影響含蓉,放射性物質(zhì)發(fā)生泄漏项郊。R本人自食惡果不足惜斟赚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一拗军、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧发侵,春花似錦、人聲如沸盅弛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)讨盒。三九已至催植,卻和暖如春勺择,著一層夾襖步出監(jiān)牢的瞬間省核,已是汗流浹背气忠。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工旧噪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淘钟,地道東北人米母。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓妙色,卻偏偏與公主長(zhǎng)得像慧耍,于是被迫代替她去往敵國(guó)和親芍碧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子师枣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 設(shè)計(jì)模式之六大原則(轉(zhuǎn)載) 關(guān)于設(shè)計(jì)模式的六大設(shè)計(jì)原則的資料網(wǎng)上很多...
    霄霄霄霄閱讀 894評(píng)論 0 1
  • 設(shè)計(jì)模式六大原則 設(shè)計(jì)模式六大原則(1):?jiǎn)我宦氊?zé)原則 定義:不要存在多于一個(gè)導(dǎo)致類變更的原因践美。通俗的說(shuō)洗贰,即一個(gè)類...
    viva158閱讀 763評(píng)論 0 1
  • 轉(zhuǎn)載標(biāo)注聲明:http://www.uml.org.cn/sjms/201211023.asp 目錄:[設(shè)計(jì)模式六...
    Bloo_m閱讀 706評(píng)論 0 7
  • 做后臺(tái)產(chǎn)品的時(shí)候敛滋,為了避免設(shè)計(jì)地凌亂绎晃,想了解一些系統(tǒng)設(shè)計(jì)模式思想,于是找高內(nèi)聚低耦合相關(guān)的文章庶艾。這篇文章是摘自網(wǎng)友...
    徐薇薇閱讀 2,179評(píng)論 0 0
  • IoT是什么 我們先來(lái)看看IoT(Internet of Thing 物聯(lián)網(wǎng))是什么。這是一個(gè)被炒作了很久的概念煤裙。...
    朱晨閱讀 721評(píng)論 0 2