當(dāng)談到開發(fā)問題時(shí),人們總會(huì)談?wù)摳鞣N定律。但對(duì)于大多數(shù)人來說,總有一些是你不了解的,這個(gè)問題就需要使用程序員最喜歡的方法解決了:最近 GitHub 上的一個(gè)「定律合集」項(xiàng)目突然登上了趨勢(shì)榜第二位,Star 數(shù)上千,該項(xiàng)目對(duì)一些最常見的定律進(jìn)行了概括,詳情見下文。 大家都是資深程序員,以后就不要老念叨「真香定律」了。
本文包含對(duì)一些定律、原則和模式的解釋,但并不主張其中任何一項(xiàng)。是否要應(yīng)用哪個(gè)定律一直是一個(gè)爭(zhēng)論性問題,并且很大程度上取決于你在做哪方面的工作。 這些規(guī)則目錄如下: 定律
原則
對(duì)于以上如此多的定律和原則,我們選取了其中一些定律和所有的原則進(jìn)行編譯。現(xiàn)在,先簡(jiǎn)單看一下定律吧↓↓ 定律 阿姆達(dá)爾定律 維基百科:計(jì)算機(jī)科學(xué)界的經(jīng)驗(yàn)法則,因吉恩·阿姆達(dá)爾而得名。它代表了處理器并行運(yùn)算之后效率提升的能力。阿姆達(dá)爾定律是固定負(fù)載(計(jì)算總量不變時(shí))時(shí)的量化標(biāo)準(zhǔn)。 舉例說明,如果一個(gè)程序由兩部分(A和B)組成,A必須有單個(gè)處理器執(zhí)行,B可以并行執(zhí)行,那么在執(zhí)行該程序的系統(tǒng)中增加多個(gè)處理器帶來的好處是有限的。它可能會(huì)大大提升B的速度,但A的速度會(huì)保持不變。如下如所示: 從圖中可以看出,一個(gè)程序如果只有50%的部分可并行處理,那它使用10個(gè)以上處理單元時(shí),處理速度將不會(huì)有很大的提升。而一個(gè)程序如果有95%的部分可并行處理,即使使用的處理單元超過1000個(gè),其處理速度也會(huì)顯著提高。 侯世達(dá)定律 維基百科:做事所花費(fèi)的時(shí)間總是比你預(yù)期的要長(zhǎng),即使你的預(yù)期中考慮了侯世達(dá)定律。 當(dāng)你估計(jì)做一件事要花費(fèi)多長(zhǎng)時(shí)間時(shí),可能會(huì)想到這一定律。軟件開發(fā)中的老生常談是,我們往往很難估計(jì)交付某個(gè)東西所需要的準(zhǔn)確時(shí)間。 炒作周期 & 阿瑪拉定律 維基百科:我們總是高估一項(xiàng)技術(shù)的短期效益,而低估其長(zhǎng)期效果。 此定律被描述為鼓勵(lì)人們思考科技所能帶來的長(zhǎng)期影響的言論。同時(shí),阿瑪拉定律也被人們稱為是對(duì)“炒作周期”(技術(shù)成熟度曲線)最形象的說明。如下圖所示: 簡(jiǎn)言之,這一周期表明,新技術(shù)及其潛在影響通常會(huì)引起一陣興奮。然后很多團(tuán)隊(duì)快速投入該技術(shù),然后有時(shí)會(huì)對(duì)結(jié)果感到失望。這可能是因?yàn)榧夹g(shù)還不夠成熟,也可能是因?yàn)楝F(xiàn)實(shí)應(yīng)用還沒有完全實(shí)現(xiàn)。 經(jīng)過一段時(shí)間后,技術(shù)的能力增加,使用技術(shù)的實(shí)際機(jī)會(huì)也增加,置身其中的團(tuán)隊(duì)最終開始受益。 海勒姆法則 網(wǎng)絡(luò)定義:當(dāng)一個(gè) API 有足夠多的用戶時(shí),你在約定中承諾什么都無所謂,所有在你系統(tǒng)里面被觀察到的行為都會(huì)被一些用戶直接依賴。 海勒姆法則指出,當(dāng)你的API有非常多的用戶時(shí),API中的所有行為最終都會(huì)被某個(gè)人所依賴。舉個(gè)簡(jiǎn)單的例子:非功能性元素,如API的響應(yīng)時(shí)間。稍微復(fù)雜一點(diǎn)的例子:依賴對(duì)錯(cuò)誤消息應(yīng)用正則表達(dá)式來確定API錯(cuò)誤類型的用戶。 摩爾定律 該定律認(rèn)為,集成電路中的晶體管數(shù)量大約每?jī)赡攴环?/p> 該定律通常用來表示半導(dǎo)體和芯片技術(shù)發(fā)展的絕對(duì)速度。從上世紀(jì)70年代到90年代末,摩爾的預(yù)測(cè)一直非常精準(zhǔn)。但近年來,該趨勢(shì)已經(jīng)發(fā)生了細(xì)微的變化,部分原因在于組件小型化程度受到的物理限制。然而,并行化的發(fā)展以及半導(dǎo)體技術(shù)和量子計(jì)算領(lǐng)域潛在的革命性變化,可能意味著摩爾定律在未來幾十年仍將適用。 復(fù)雜性守恒定律(泰斯勒定律) 該定律認(rèn)為每個(gè)系統(tǒng)內(nèi)都有一定的復(fù)雜性不可減少。 系統(tǒng)中的某些復(fù)雜性是“不經(jīng)意的”??赡苁怯山Y(jié)構(gòu)不良、錯(cuò)誤或只是建模不良造成的結(jié)果。這些不經(jīng)意造成的復(fù)雜性是可以減少(或消除)的。但是,有些復(fù)雜性是“固有的”,是由亟待解決問題的內(nèi)在復(fù)雜性造成的。而這種復(fù)雜性可以移動(dòng),但無法消除。 這個(gè)定律有趣的一點(diǎn)在于,即使簡(jiǎn)化整個(gè)系統(tǒng),也無法降低內(nèi)在的復(fù)雜性。這種方法只不過是將復(fù)雜性轉(zhuǎn)移到了用戶一方,然后用戶必須以更復(fù)雜的方式行事。 帕金森瑣碎定律 該定律認(rèn)為,大型組織會(huì)花費(fèi)大量時(shí)間和精力來討論無關(guān)緊要的瑣事,但是真正重大的決議反而可以輕松過關(guān)。 這是因?yàn)椋谟懻摲浅I(yè)而且金額龐大的事情時(shí),一般人由于缺乏專業(yè)知識(shí),不敢隨便發(fā)言,以免失言,貽笑大方,因此多半都會(huì)肯定(或逃避)該重大方案,而提些與主題無關(guān)的雞毛蒜皮小事。相對(duì)的,對(duì)于簡(jiǎn)單的瑣碎小事,由于平常大家都會(huì)接觸到而且有相當(dāng)?shù)恼J(rèn)識(shí),反而意見特別多,帕金森稱此現(xiàn)象為瑣碎定律。 Unix 哲學(xué) Unix哲學(xué)認(rèn)為,軟件組件應(yīng)該很小,而且應(yīng)該把注意力放在具體的事件上。將小的、簡(jiǎn)單的、定義良好的單元組合在一起,而不是使用大的、復(fù)雜的、多用途程序,這樣可以使構(gòu)建系統(tǒng)變得更加容易。 像“微服務(wù)架構(gòu)”這樣的現(xiàn)代實(shí)踐就應(yīng)用了這種哲學(xué)。在該架構(gòu)中,服務(wù)很小,且集中做某件事,使得復(fù)雜的行為由簡(jiǎn)單的構(gòu)件組成。 Wadler 定律 該定律認(rèn)為:在任何語言設(shè)計(jì)中,討論這個(gè)列表中某個(gè)特性所花費(fèi)的總時(shí)間與它位置的冪成正比。 1.語義 2.語法 3.詞匯語法 4.注釋的詞匯語法 (簡(jiǎn)言之,如果在語義上花1小時(shí)來討論,那將在注釋語法上花8小時(shí))。 與帕金森瑣碎定律相似,Wadler 定律認(rèn)為,當(dāng)設(shè)計(jì)一種語言時(shí),花在語言結(jié)構(gòu)上的時(shí)間與這些特征的重要性相比很不成比例。 原則 在這篇文章中,作者表示原則是指導(dǎo)程序猿開發(fā)新應(yīng)用的一些方針。在碼代碼的過程中,我們經(jīng)常會(huì)遇到各種困難,當(dāng)然也有各種約定俗成的規(guī)則。例如最簡(jiǎn)單的命名法,有的默認(rèn)為使用下劃線、使用小駝峰或大駝峰式的命名,只有了解這些規(guī)則,編寫的代碼才是優(yōu)美的。 穩(wěn)健性原則 在維基百科中,穩(wěn)健性原則(Robustness Principle)描述為:“寫代碼要保守,要能接受其它方面的各種信息”。 該原則通常應(yīng)用于服務(wù)器應(yīng)用的開發(fā),它表示發(fā)送的內(nèi)容應(yīng)該盡可能少且符合要求。但如果可以處理不符合要求的輸入,那么你的目標(biāo)應(yīng)該希望允許各種非一致性的輸入。 該原則的目標(biāo)是構(gòu)建一個(gè)穩(wěn)健的系統(tǒng),對(duì)于輸入端,只要對(duì)方的意圖仍可以理解,那么我們就應(yīng)該需要處理非標(biāo)準(zhǔn)格式的輸入。然而,接受格式錯(cuò)誤的輸入有潛在的安全影響,特別是當(dāng)這種輸入的處理方式還沒有經(jīng)過良好的測(cè)試。 SOLID 該原則是一個(gè)縮寫,即:
如上所示, SOLID 指代了面向?qū)ο缶幊毯兔嫦驅(qū)ο笤O(shè)計(jì)的五個(gè)基本原則。當(dāng)這些原則被一起應(yīng)用時(shí),它們使得程序員能開發(fā)更容易進(jìn)行軟件維護(hù)和擴(kuò)展的系統(tǒng)。SOLID常應(yīng)用在測(cè)試的驅(qū)動(dòng)開發(fā)上,并且是敏捷開發(fā)以及自適應(yīng)軟件開發(fā)的基本原則的重要組成部分。下面讓我們看看這5個(gè)基本原則都是什么吧。 單一功能原則 在維基百科的描述中,單一功能原則(Single responsibility principle)規(guī)定每個(gè)類都應(yīng)該有一個(gè)單一的功能,并且該功能應(yīng)該由這個(gè)類完全封裝起來。所有它的(這個(gè)類的)服務(wù)都應(yīng)該嚴(yán)密的和該功能平行(功能平行,意味著沒有依賴)。 這一原則表明模塊或類應(yīng)該只完成一件事。這意味著對(duì)程序特性的單個(gè)小修正,應(yīng)該只需要在一個(gè)組件中進(jìn)行更改。例如,更改驗(yàn)證密碼的方式應(yīng)該只需要更改程序特定的某個(gè)模塊。 保持一個(gè)類專注于單一功能點(diǎn),這樣做的重要原因是它會(huì)使得類更加穩(wěn)健。如果我們知道正在修改的組件只有一個(gè)功能,那么測(cè)試會(huì)變得更簡(jiǎn)單,新的修改也就好處理了。例如上面,修改密碼驗(yàn)證應(yīng)該只影響與密碼驗(yàn)證相關(guān)的特性,如果我們要對(duì)具有多功能的模塊進(jìn)行修改,這樣進(jìn)行推斷就要復(fù)雜多了。 開閉原則 在面向?qū)ο蟮木幊讨校_閉原則規(guī)定:軟件中的對(duì)象(類、模塊、函數(shù)等等)對(duì)于擴(kuò)展應(yīng)該是開放的,但是對(duì)已存行為的修改是封閉的。這意味著,一個(gè)實(shí)體需要允許在不改變?cè)创a的前提下變更它的行為。 該特性在產(chǎn)品化的環(huán)境中特別有價(jià)值,因?yàn)樵诋a(chǎn)品化中改變?cè)创a需要代碼審查,例如單元測(cè)試等方法確保產(chǎn)品使用的質(zhì)量。遵循這種原則的代碼在擴(kuò)展時(shí)并不發(fā)生改變,因此無需上述過程。 舉個(gè)栗子,假設(shè)某個(gè)模塊能夠?qū)?Markdown 文本轉(zhuǎn)換為HTML。如果模塊可以擴(kuò)展新的特性,即能處理新提出的Markdown 特性而不修改模塊內(nèi)部,那么這就表示它對(duì)擴(kuò)展是開放的。 這一原則與面向?qū)ο蟮木幊烫貏e相關(guān),我們可以設(shè)計(jì)易于擴(kuò)展的對(duì)象,但也要避免設(shè)計(jì)不穩(wěn)定的對(duì)象,因?yàn)樗鼈兊默F(xiàn)有行為可能會(huì)以意想不到的方式發(fā)生改變。 里氏替換原則 里氏替換原則(Liskov Substitution principle)是對(duì)子類型的特別定義。里氏替換原則的內(nèi)容可以描述為: 派生類(子類)對(duì)象可以在程序中代替其基類(父類)對(duì)象。也就是說,如果一個(gè)模塊依賴于某個(gè)類,那么該模塊就需要能使用該類的派生類,且不會(huì)發(fā)生系統(tǒng)錯(cuò)誤。 舉個(gè)栗子,如果我們有一種方法,它可以從表征文件的結(jié)構(gòu)中讀取XML文本。如果該方法的基類是“file”,那么它能調(diào)用從“file”派生的任意類。 這一原則對(duì)于面向?qū)ο蟮木幊谭浅V匾?,我們必須仔?xì)建模類的層次結(jié)構(gòu),以避免讓系統(tǒng)用戶感到困惑。 接口隔離原則 接口隔離原則(interface-segregation principles)指明用戶(client)應(yīng)該不依賴于它不使用的方法。接口隔離原則拆分龐大臃腫的接口成為更小的和更具體的接口,這樣用戶將會(huì)只需要知道他們感興趣的方法。這種縮小的接口也被稱為角色接口(role interfaces)。接口隔離原則的目的是系統(tǒng)解開耦合,從而容易重構(gòu)、更改和重新部署。 舉個(gè)栗子,假設(shè)我們有一種能夠從表征文件的結(jié)構(gòu)中讀取XML文檔的方法。這種方法只需要讀取字節(jié)以及在文件中前移或后移即可。如果該方法因?yàn)槲募Y(jié)構(gòu)的一種非相關(guān)性特征改變而需要進(jìn)行更新(如用于表征文件安全的權(quán)限模型的更新),則該原則無效。文件最好實(shí)現(xiàn)‘seekable-stream’接口并讓XML reader使用。 該原則與面向?qū)ο缶幊叹哂刑厥獾南嚓P(guān)性,其中接口、層次和抽象類型用于最小化不同組件之間的耦合。鴨子類型(duck typing)通過消除顯式接口來執(zhí)行該原則。 依賴反轉(zhuǎn)原則 在傳統(tǒng)的應(yīng)用架構(gòu)中,低層次的組件設(shè)計(jì)用到高層次的組件中,這一點(diǎn)提供了逐步的構(gòu)建一個(gè)復(fù)雜系統(tǒng)的可能。在這種結(jié)構(gòu)下,高層次的組件直接依賴于低層次的組件去實(shí)現(xiàn)一些任務(wù)。這種對(duì)于低層次組件的依賴限制了高層次組件被重用的可行性。 依賴反轉(zhuǎn)原則的目的是把高層次組件從對(duì)低層次組件的依賴中解耦出來,這樣使得重用不同層級(jí)的組件實(shí)現(xiàn)變得可能。把高層組件和低層組件劃分到不同的包/庫,該方式也促進(jìn)了這種解耦。由于低層組件是對(duì)高層組件接口的具體實(shí)現(xiàn),因此低層組件包的編譯是依賴于高層組件的,這顛倒了傳統(tǒng)的依賴關(guān)系。眾多的設(shè)計(jì)模式,比如插件、服務(wù)定位器或者依賴反轉(zhuǎn),則被用來在運(yùn)行時(shí)把指定的低層組件實(shí)現(xiàn)提供給高層組件。 具體而言,依賴反轉(zhuǎn)原則規(guī)定:
舉個(gè)栗子,如果我們有一個(gè)從網(wǎng)站讀取元數(shù)據(jù)的程序,且主組件包含下載網(wǎng)站內(nèi)容的組件和讀取元數(shù)據(jù)的組件。如果我們考慮依賴反轉(zhuǎn)原則,那么主組件只能依賴于某些抽象組件,其中某個(gè)抽象組件只能獲取比特?cái)?shù)據(jù)、另一個(gè)只能從比特流中讀取元數(shù)據(jù)。主組件并不知道任何關(guān)于TCP/IP、HTTP或HTML等協(xié)議或格式的相關(guān)信息。 這一原則是比較復(fù)雜的,因?yàn)樗坪醴崔D(zhuǎn)了系統(tǒng)的依賴性關(guān)系。在實(shí)踐中,該原則意味著獨(dú)立的編排模塊必須確保使用了正確的抽象類型實(shí)現(xiàn)。例如在上面的例子中,元數(shù)據(jù)讀取模塊還是需要一些抽象類型的實(shí)現(xiàn),即HTTP文件下載器和HTML元標(biāo)簽讀取器。 |
|