在21世紀(jì)的前幾年里授段,“Uncle Bob”Robert Martin引入了用OOP開發(fā)軟件的五條原
則,其目的是設(shè)計(jì)出更易于維護(hù)的高質(zhì)量系統(tǒng)番甩。無(wú)論是設(shè)計(jì)新應(yīng)用程序侵贵,還是重構(gòu)現(xiàn)有基
本代碼,這些SOLID原則都成為開發(fā)人員的地圖缘薛。
一 單一職責(zé)原則
單一職責(zé)原則(Single Responsibility Principle窍育,SRP)指出,每個(gè)方法或類應(yīng)當(dāng)有且僅有
一個(gè)改變的理由宴胧。這意味著每個(gè)方法或類應(yīng)當(dāng)做一件事情漱抓,或者只有一項(xiàng)職責(zé)。在所有的
SOLID原則中恕齐,這是大多數(shù)開發(fā)人員感到最能完全理解的一條乞娄。嚴(yán)格來說,這也可能是違
反最頻繁的一條原則了显歧。
二 開放閉合原則
開放/封閉原則(Open/Close Principle仪或,OCP)是指軟件(方法、類等)應(yīng)當(dāng)開放擴(kuò)充且關(guān)閉
修改追迟。如果覺得它非常類似于繼承的OOP 原則,那就對(duì)了骚腥。它們之間的關(guān)系非常密切敦间。事
實(shí)上,在.NET中OCP就是依賴于繼承的。
OCP的要點(diǎn)在于:作為開發(fā)人員廓块,別人偶爾會(huì)向我們提供基類厢绝,偶爾也會(huì)為其他開發(fā)人
員生成基類框架,供其使用带猴。這些使用者應(yīng)當(dāng)僅能使用這些基類昔汉,但不能對(duì)其進(jìn)行修改。
這一點(diǎn)是必要的拴清,因?yàn)槠渌褂谜咭部赡芤蕾囉谟苫愄峁┑墓δ馨胁 H绻试S使用者修改
這些基類,可能會(huì)導(dǎo)致連鎖反應(yīng)口予,不僅會(huì)影響到應(yīng)用程序中的各方面娄周,還會(huì)影響到企業(yè)內(nèi)
的應(yīng)用程序。還有一個(gè)問題沪停,使用者有時(shí)可能會(huì)收到基類的升級(jí)版本煤辨。使用者在升級(jí)之前,
必須找出一種方法用來處理其對(duì)該基類先前版本中所做的自定義木张。
于是众辨,問題變?yōu)椋骸澳敲矗绻倚枰薷倪@個(gè)基類的工作方式舷礼,那應(yīng)當(dāng)怎么做呢鹃彻?”
OCP的另一部分中給出這一答案;基類應(yīng)當(dāng)開放且轨,可進(jìn)行擴(kuò)充浮声。在這里,擴(kuò)充是指創(chuàng)建一
個(gè)由此基類繼承而來的派生類旋奢,它可以擴(kuò)充或重載基類功能泳挥,以提供使用者所需要的特定
功能。這樣至朗,使用者就能使用類的修改版本屉符,而不會(huì)影響到類的其他使用者。使用者還可
以在將來更輕松地使用基類的升級(jí)版本锹引,因?yàn)樗麄儾挥脫?dān)心丟失自己的修改內(nèi)容矗钟。
三 里氏代換原則
繼承對(duì)于OCP,就相當(dāng)于多態(tài)性對(duì)于里氏替換原則(Liskov Substitution Principle嫌变,LSP)吨艇。
LSP 規(guī)定:用超類代替應(yīng)用程序中使用的對(duì)象時(shí),應(yīng)當(dāng)不會(huì)破壞應(yīng)用程序腾啥。這通常也被稱
為“契約式設(shè)計(jì)(design by contract)”东涡。
回想前面的多態(tài)性示例冯吓,ComputePay 方法使用了Employee 類型的列表,其中Employee
就是基類型(超類型)疮跑。Salary组贺、Hourly 和Seasonal 類都是從Employee 繼承而來,因此它們
是Employee 的子類型祖娘。
根據(jù)LSP失尖,即使已經(jīng)將列表聲明為Employee 的列表,也仍然可以用Salary渐苏、Hourly
和Seasonal 的具體實(shí)例來填充它掀潮。因?yàn)橛辛死^承,它們都支持Employee 聲明的相同契約(公
共的方法集或API)整以。應(yīng)用程序可以對(duì)該列表進(jìn)行迭代胧辽,并調(diào)用那些在列表中各個(gè)項(xiàng)目的
Employee 上定義的方法,不需要知道或特別關(guān)心它們都是什么類型公黑。如果它們支持契約邑商,
該調(diào)用就是合法的。
四 接口分離原則
到目前為止凡蚜,已經(jīng)在示例中使用了基于類的繼承人断,但還沒有過多地討論接口〕回想一
下恶迈,接口就是在代碼中定義的契約,而類同意實(shí)現(xiàn)這一契約谱醇。這份協(xié)議要求類來為接口中
定義的所有方法提供實(shí)現(xiàn)暇仲。至于如何實(shí)現(xiàn)方法,則由這個(gè)類來決定副渴,只要它遵守契約奈附,支
持接口中的定義即可。接口是.NET中功能非常強(qiáng)大的功能煮剧;它們對(duì)繼承和多態(tài)的支持方式
與類相同斥滤。
接口分離原則(Interface Segregation Principle,ISP)規(guī)定勉盅,不應(yīng)當(dāng)強(qiáng)制客戶端依賴于其
不使用的接口佑颇。例如,銀行系統(tǒng)可能有一個(gè)用于評(píng)估信用申請(qǐng)的服務(wù)草娜。為便于討論挑胸,假定
該服務(wù)不僅處理有質(zhì)押信用(車船貸款、抵押)宰闰,也處理無(wú)質(zhì)押信用(信用卡茬贵、信用證凸克、股票
信用額度)。如果正在開發(fā)一個(gè)客戶端闷沥,用于幫助從事汽車代理的金融專員為其客戶獲得汽
車貸款,則只需要關(guān)注汽車貸款的申請(qǐng)即可咐容,無(wú)需考慮有關(guān)這一服務(wù)的任何其他事情舆逃。如
果沒有ISP,應(yīng)用程序可能必須了解其他方法戳粒。
盡管乍看起來這并沒有什么路狮,但它至少是增加了應(yīng)用程序的復(fù)雜性,因?yàn)閾?jù)以進(jìn)行開
發(fā)的API中會(huì)有許多方法蔚约,遠(yuǎn)遠(yuǎn)超出所需要的奄妨。這樣可能會(huì)導(dǎo)致混淆,調(diào)用錯(cuò)誤的方法還
可能會(huì)導(dǎo)致潛在的錯(cuò)誤苹祟。還有一種可能砸抛,API中未被應(yīng)用程序用到的部分可能會(huì)改變,而
這又會(huì)導(dǎo)致對(duì)終端的改變树枫。這樣直焙,因?yàn)闆]用到、沒想用砂轻、甚至是根本就不關(guān)心的一些功能奔誓,
而增加了應(yīng)用程序的維護(hù)成本。這種情況還存在安全風(fēng)險(xiǎn)搔涝。該應(yīng)用程序是專用于汽車貸款
的厨喂。如果不道德的開發(fā)人員利用這個(gè)過于龐大的API來允許利用這一申請(qǐng)擔(dān)保其他類型的
信用,又該怎么辦呢庄呈?這種問題的嚴(yán)重性就不僅僅是代碼癱瘓蜕煌、不可維護(hù)那么簡(jiǎn)單了。
這一問題的解決方案就是專門針對(duì)客戶端的需要抒痒,為該服務(wù)創(chuàng)建幾個(gè)更小的幌绍、更精細(xì)
的接口。對(duì)于該示例應(yīng)用程序故响,專門設(shè)計(jì)一個(gè)針對(duì)汽車貸款的接口是比較適當(dāng)?shù)淖龇恪?yīng)
用程序可以用同一實(shí)現(xiàn)訪問同一個(gè)類,但這一次它使用了一個(gè)特定的接口彩届,其中僅有實(shí)際
服務(wù)的一部分方法伪冰。這樣就降低了復(fù)雜性,將應(yīng)用程序與API其他部分的修改隔離開來樟蠕,
還有助于堵塞安全漏洞贮聂。
五 依賴倒轉(zhuǎn)原則
在完美世界里靠柑,應(yīng)用程序的組件之間沒有耦合關(guān)系或綁定關(guān)系。開發(fā)人員也能夠改變
自己希望改變的任何東西吓懈,而不需要擔(dān)心在應(yīng)用程序的其他地方出現(xiàn)缺陷歼冰,或者“不希望
存在的負(fù)面影響”。令人悲傷的是耻警,我們并不是生活在完美世界里隔嫡。因此,組件需要相互
綁定在一起甘穿,或者在某一點(diǎn)耦合腮恩,以構(gòu)成實(shí)際應(yīng)用程序。
依賴倒置原則(Dependency Inversion Principle温兼,DIP)規(guī)定:代碼應(yīng)當(dāng)取決于抽象概念秸滴,
而不是具體實(shí)現(xiàn);這些抽象不應(yīng)當(dāng)依賴于細(xì)節(jié)募判;而細(xì)節(jié)應(yīng)當(dāng)依賴于抽象荡含。類可能依賴于其
他類來執(zhí)行其工作(Employee 服務(wù)可能依賴于數(shù)據(jù)訪問組件向數(shù)據(jù)存儲(chǔ)中保存和檢索員工
信息)。但是届垫,它們不應(yīng)當(dāng)依賴于該類的特定具體實(shí)現(xiàn)内颗,而應(yīng)當(dāng)是它的抽象。也就是說敦腔,
Employee 服務(wù)不知道(或不關(guān)心)正在使用哪個(gè)具體的數(shù)據(jù)訪問組件——只有它的抽象或代
碼契約(或接口)支持那些用于保存和檢索員工所需要的方法均澳。
顯然,這一概念會(huì)大大提高系統(tǒng)的靈活性符衔。如果類只關(guān)心它們用于支持特定契約而不
是特定類型的組件找前,就可以快速而輕松地修改這些低級(jí)服務(wù)的功能,同時(shí)最大限度地降低
對(duì)系統(tǒng)其余部分的影響判族。在第6 章躺盛,還會(huì)看到如何利用這一概念來模擬這些依賴項(xiàng),以進(jìn)
行測(cè)試形帮。有時(shí)槽惫,需要向類中提供這一低級(jí)服務(wù)的具體實(shí)現(xiàn),以便這個(gè)類能夠完成自己的工
作辩撑。最常見的做法界斜,特別是在.NET中使用TDD 的開發(fā)人員,就是依賴項(xiàng)注入(DI)模式合冀。
============以上OOP軟件設(shè)計(jì)的SOLID原則==============
控制反轉(zhuǎn)原則
全局注冊(cè)樹
ORM 對(duì)象數(shù)據(jù)映射
合成聚合原則
啟動(dòng)和執(zhí)行分離原則
面向接口編程
配置加載 不是硬編程原則