聊聊編程范式

什么是編程范式

編程范式一詞最早來(lái)自 Robert Floyd 在 1979 年圖靈獎(jiǎng)的頒獎(jiǎng)演說(shuō),是程序員看待程序應(yīng)該具有的觀點(diǎn)蕴忆,代表了程序設(shè)計(jì)者認(rèn)為程序應(yīng)該如何被構(gòu)建和執(zhí)行的看法茧跋,與軟件建模方式和架構(gòu)風(fēng)格有緊密關(guān)系慰丛。

現(xiàn)在主流的編程范式有三種:

  • 結(jié)構(gòu)化編程(structured programming)
  • 面向?qū)ο缶幊蹋╫bject-oriented programming)
  • 函數(shù)式編程(functional programming)

這幾種編程范式之間的關(guān)系如下:


relations.png

如果你對(duì)上圖中編程范式之間的關(guān)系已理解得非常透徹,那就沒(méi)有必要再往下看了瘾杭,否則建議耐心看完本文璧帝,在過(guò)程中可以跳過(guò)熟悉的章節(jié)。

眾所周知富寿,計(jì)算機(jī)運(yùn)行在圖靈機(jī)模型之上。最初锣夹,程序員通過(guò)紙帶將指令和數(shù)據(jù)輸入到計(jì)算機(jī)页徐,計(jì)算機(jī)執(zhí)行指令,完成計(jì)算银萍。后來(lái)变勇,程序員編寫(xiě)程序(包括指令和數(shù)據(jù)),將程序加載到計(jì)算機(jī),計(jì)算機(jī)執(zhí)行指令搀绣,完成計(jì)算飞袋。時(shí)至今日,軟件已經(jīng)非常復(fù)雜链患,規(guī)模也很大巧鸭,人們通過(guò)軟件來(lái)解決各個(gè)領(lǐng)域(Domain)的問(wèn)題,比如通信麻捻,嵌入式纲仍,銀行,保險(xiǎn)贸毕,交通郑叠,社交,購(gòu)物等明棍。

thinking-base.png

人們把一個(gè)個(gè)具體的領(lǐng)域問(wèn)題跑在圖靈機(jī)模型上乡革,然后做計(jì)算,而領(lǐng)域問(wèn)題和圖靈機(jī)模型之間有一個(gè)很大的 gap(What摊腋,How沸版,Why),這是程序員主要發(fā)揮的場(chǎng)所歌豺。編程范式是程序員的思維底座推穷,決定了設(shè)計(jì)元素和代碼結(jié)構(gòu)。程序員把領(lǐng)域問(wèn)題映射到某個(gè)編程范式之上类咧,然后通過(guò)編程語(yǔ)言來(lái)實(shí)現(xiàn)馒铃。顯然,編程范式到圖靈機(jī)模型的轉(zhuǎn)化都由編譯器來(lái)完成痕惋,同時(shí)這個(gè)思維底座越高区宇,程序員做的就會(huì)越少。

你可能會(huì)有一個(gè)疑問(wèn):為什么會(huì)有多個(gè)編程范式值戳?換句話說(shuō)议谷,就是程序員為什么需要多個(gè)思維底座,而不是一個(gè)堕虹?

思維底座取決于程序員看待世界的方式卧晓,和哲學(xué)及心里學(xué)都有關(guān)。程序員開(kāi)發(fā)軟件是把現(xiàn)實(shí)中的世界模擬到計(jì)算機(jī)中來(lái)運(yùn)行赴捞,每個(gè)程序員在這個(gè)時(shí)候都相當(dāng)于一個(gè)造物主逼裆,在計(jì)算機(jī)重新創(chuàng)造一個(gè)特定領(lǐng)域的世界,那么如何看待這個(gè)世界就有些哲學(xué)觀的味道在里面赦政。這個(gè)虛擬世界的最小構(gòu)筑物是什么胜宇?每個(gè)構(gòu)筑物之間的關(guān)系是什么?用什么方式把這個(gè)虛擬世界層累起來(lái)。隨著科學(xué)技術(shù)的演進(jìn)桐愉,人們看待世界的方式會(huì)發(fā)生變化财破,比如生物學(xué)已經(jīng)演進(jìn)到細(xì)胞,自然科學(xué)已經(jīng)演進(jìn)到原子从诲,于是程序員模擬世界的思維底座也會(huì)發(fā)生變化左痢。

程序員模擬的世界最終要跑在圖靈機(jī)模型上,這就有經(jīng)濟(jì)學(xué)的要求盏求,成本越小越好抖锥。資源在任何時(shí)候都是有限的,性能是有約束的碎罚,不同的編程范式有不同的優(yōu)缺點(diǎn)磅废,程序員在解決領(lǐng)域問(wèn)題時(shí)需要有多個(gè)思維底座來(lái)進(jìn)行權(quán)衡取舍,甚至融合荆烈。

為了能更深刻的理解編程范式拯勉,我們接下來(lái)一起回顧一下編程范式的簡(jiǎn)史。

編程范式簡(jiǎn)史

paradigm-history.png

機(jī)器語(yǔ)言使用 0 和 1 組成的二進(jìn)制序列來(lái)表達(dá)指令憔购,非彻停晦澀難懂。匯編語(yǔ)言使用助記符來(lái)表達(dá)指令玫鸟,雖然比機(jī)器語(yǔ)言進(jìn)步了一些导绷,但編寫(xiě)程序仍然是一件非常痛苦的事情。匯編語(yǔ)言可以通過(guò)匯編(編譯)得到機(jī)器語(yǔ)言屎飘,機(jī)器語(yǔ)言可以通過(guò)反匯編得到匯編語(yǔ)言妥曲。匯編語(yǔ)言和機(jī)器語(yǔ)言一一對(duì)應(yīng),都是直接面向機(jī)器的低級(jí)語(yǔ)言钦购,最貼近圖靈機(jī)模型檐盟。

站在結(jié)構(gòu)化編程的視角,機(jī)器語(yǔ)言和匯編語(yǔ)言也是有編程范式的押桃,它們的編程范式就是非結(jié)構(gòu)化編程葵萎。當(dāng)時(shí) goto 語(yǔ)句滿天飛,程序及其難以維護(hù)唱凯。后來(lái)羡忘,大家對(duì)于 goto 語(yǔ)句是有害的達(dá)成了共識(shí),就從編程語(yǔ)言設(shè)計(jì)上把 goto 語(yǔ)句拿掉了磕昼。

隨著計(jì)算機(jī)技術(shù)的不斷發(fā)展壳坪,人們開(kāi)始尋求與機(jī)器無(wú)關(guān)且面向用戶的高級(jí)語(yǔ)言。無(wú)論何種機(jī)型的計(jì)算機(jī), 只要配備上相應(yīng)高級(jí)語(yǔ)言的編譯器掰烟,則用該高級(jí)語(yǔ)言編寫(xiě)的程序就可以運(yùn)行。首先被廣泛使用的高級(jí)語(yǔ)言是 Fortran,有效的降低了編程門檻纫骑,極大的提升了編程效率蝎亚。后來(lái) C 語(yǔ)言橫空出世,它提供了對(duì)于計(jì)算機(jī)而言較為恰當(dāng)?shù)某橄笙裙荩帘瘟擞?jì)算機(jī)硬件的諸多細(xì)節(jié)发框,是結(jié)構(gòu)化編程語(yǔ)言典型代表。時(shí)至今日煤墙,C 語(yǔ)言依然被廣泛使用梅惯。

當(dāng)高級(jí)語(yǔ)言大行其道以后,人們開(kāi)發(fā)的程序規(guī)模逐漸膨脹仿野,這時(shí)如何組織程序變成了新的挑戰(zhàn)铣减。有一種語(yǔ)言搭著 C 語(yǔ)言的便車將面向?qū)ο蟮脑O(shè)計(jì)風(fēng)格帶入主流視野,這就是 C++脚作,它完全兼容 C 語(yǔ)言葫哗。在很長(zhǎng)一段時(shí)間內(nèi),C++ 風(fēng)頭十足球涛,成為行業(yè)中最主流的編程語(yǔ)言劣针。后來(lái),計(jì)算機(jī)硬件的能力得到了大幅提升亿扁,Java 語(yǔ)言脫穎而出捺典。Java 語(yǔ)言假設(shè)程序的代碼空間是開(kāi)放的,在 JVM 虛擬機(jī)上運(yùn)行从祝,一方面支持面向?qū)ο蠼蠹海硪环矫嬷С?GC 功能。

不難看出哄褒,編程語(yǔ)言的發(fā)展就是一個(gè)逐步遠(yuǎn)離計(jì)算機(jī)硬件稀蟋,向著待解決的領(lǐng)域問(wèn)題靠近的過(guò)程。所以呐赡,編程語(yǔ)言后續(xù)的發(fā)展方向就是探索怎么更好的解決領(lǐng)域問(wèn)題退客。

前面說(shuō)的這些編程語(yǔ)言只是編程語(yǔ)言發(fā)展的主流路徑,其實(shí)還有一條不那么主流的路徑也一直在發(fā)展链嘀,那就是函數(shù)式編程語(yǔ)言萌狂,這方面的代表是 Lisp。首先怀泊,函數(shù)式編程的主要理論基礎(chǔ)是 Lambda 演算茫藏,它是圖靈完備的;其次,函數(shù)式編程是抽象代數(shù)思維,更加接近現(xiàn)代自然科學(xué)蜂挪,使用一種形式化的方式來(lái)解釋世界猖闪,通過(guò)公式來(lái)推導(dǎo)世界本辐,極度抽象(比如 F=ma)载慈。在這條路上慨绳,很多人都是偏學(xué)術(shù)風(fēng)格的阅爽,他們關(guān)注解決方案是否優(yōu)雅挟伙,如何一層層構(gòu)建抽象楼雹。他們也探索更多的可能,垃圾回收機(jī)制就是從這里率先出來(lái)的尖阔。但函數(shù)式編程離圖靈機(jī)模型太遠(yuǎn)了贮缅,在圖靈機(jī)上的運(yùn)行性能得不到直接的支撐,同時(shí)受限于當(dāng)時(shí)硬件的性能介却,在很長(zhǎng)一段時(shí)間內(nèi)谴供,這條路上的探索都只是學(xué)術(shù)圈玩得小眾游戲,于是函數(shù)式編程在當(dāng)時(shí)被認(rèn)為是一個(gè)在工程上不成熟的編程范式筷笨。當(dāng)硬件的性能不再成為阻礙憔鬼,如何解決問(wèn)題開(kāi)始變得越來(lái)越重要時(shí),函數(shù)式編程終于和編程語(yǔ)言發(fā)展的主流路徑匯合了胃夏。促進(jìn)函數(shù)式編程引起廣泛重視還有一些其他因素轴或,比如多核 CPU 和分布式計(jì)算。

編程范式是抽象的仰禀,編程語(yǔ)言是具體的照雁。編程范式是編程語(yǔ)言背后的思想,要通過(guò)編程語(yǔ)言來(lái)體現(xiàn)答恶。編程范式的世界觀體現(xiàn)在編程語(yǔ)言的核心概念中饺蚊,編程范式的方法論體現(xiàn)在編程語(yǔ)言的表達(dá)機(jī)制中,一種編程語(yǔ)言的語(yǔ)法和風(fēng)格與其所支持的編程范式密切相關(guān)悬嗓。雖然編程語(yǔ)言和編程范式是多對(duì)多的關(guān)系污呼,但每一種編程語(yǔ)言都有自己的主流編程范式。比如包竹,C 語(yǔ)言的主流編程范式是結(jié)構(gòu)化編程燕酷,而 Java 語(yǔ)言的主流編程范式是面向?qū)ο缶幊獭3绦騿T可以打破“次元壁”周瞎,將不同編程范式中的優(yōu)秀元素吸納過(guò)來(lái)苗缩,比如在 linux 內(nèi)核代碼設(shè)計(jì)中,就將對(duì)象元素吸納了過(guò)來(lái)声诸。無(wú)論在以結(jié)構(gòu)化編程為主的語(yǔ)言中引入面向?qū)ο缶幊探囱龋€是在以面向?qū)ο缶幊虨橹鞯恼Z(yǔ)言中引入函數(shù)式編程,在一個(gè)程序中應(yīng)用多范式已經(jīng)成為一個(gè)越來(lái)越明顯的趨勢(shì)彼乌。不僅僅在設(shè)計(jì)中泻肯,越來(lái)越多的編程語(yǔ)言逐步將不同編程范式的內(nèi)容融合起來(lái)渊迁。C++ 從 C++ 11 開(kāi)始支持 Lambda 表達(dá)式,Java 從 Java 8 開(kāi)始支持 Lambda 表達(dá)式灶挟,同時(shí)新誕生的語(yǔ)言一開(kāi)始就支持多范式宫纬,比如 Scala,Go 和 Rust 等膏萧。

從結(jié)構(gòu)化編程到面向?qū)ο缶幊蹋俚胶瘮?shù)式編程蝌衔,離圖靈機(jī)模型越來(lái)越遠(yuǎn)榛泛,但抽象程度越來(lái)越高,與領(lǐng)域問(wèn)題的距離越來(lái)越近噩斟。

結(jié)構(gòu)化編程

結(jié)構(gòu)化編程曹锨,也稱作過(guò)程式編程,或面向過(guò)程編程剃允。

基本設(shè)計(jì)

在使用低級(jí)語(yǔ)言編程的年代沛简,程序員站在直接使用指令的角度去思考,習(xí)慣按照自己的邏輯去寫(xiě)斥废,指令之間可能共享數(shù)據(jù)椒楣,這其中最方便的寫(xiě)法就是需要用到哪塊邏輯就 goto 過(guò)去執(zhí)行一段代碼,然后再 goto 到另外一個(gè)地方牡肉。當(dāng)代碼規(guī)模比較大時(shí)捧灰,就難以維護(hù)了,這種編程方式便是非結(jié)構(gòu)化編程统锤。


goto.png

迪克斯特拉(E.W.dijkstra)在 1969 年提出結(jié)構(gòu)化編程毛俏,摒棄了 goto 語(yǔ)句,而以模塊化設(shè)計(jì)為中心饲窿,將待開(kāi)發(fā)的軟件系統(tǒng)劃分為若干個(gè)相互獨(dú)立的模塊煌寇,這樣使完成每一個(gè)模塊的工作變得單純而明確,為設(shè)計(jì)一些較大的軟件打下了良好的基礎(chǔ)逾雄。按照結(jié)構(gòu)化編程的觀點(diǎn)阀溶,任何算法功能都可以通過(guò)三種基本程序結(jié)構(gòu)(順序、選擇和循環(huán))的組合來(lái)實(shí)現(xiàn)嘲驾。

結(jié)構(gòu)化編程主要表現(xiàn)在一下三個(gè)方面:

  • 自頂向下淌哟,逐步求精。將編寫(xiě)程序看成是一個(gè)逐步演化的過(guò)程辽故,將分析問(wèn)題的過(guò)程劃分成若干個(gè)層次徒仓,每一個(gè)新的層次都是上一個(gè)層次的細(xì)化。
  • 模塊化誊垢。將系統(tǒng)分解成若干個(gè)模塊掉弛,每個(gè)模塊實(shí)現(xiàn)特定的功能症见,最終的系統(tǒng)由這些模塊組裝而成,模塊之間通過(guò)接口傳遞信息殃饿。
  • 語(yǔ)句結(jié)構(gòu)化谋作。在每個(gè)模塊中只允許出現(xiàn)順序、選擇和循環(huán)三種流程結(jié)構(gòu)的語(yǔ)句乎芳。

結(jié)構(gòu)化程序設(shè)計(jì)是用計(jì)算機(jī)的思維方式去處理問(wèn)題遵蚜,將數(shù)據(jù)結(jié)構(gòu)和算法分離(程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法)。數(shù)據(jù)結(jié)構(gòu)描述待處理數(shù)據(jù)的組織形式奈惑,而算法描述具體的操作過(guò)程吭净。我們用過(guò)程函數(shù)把這些算法一步一步的實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)的依次調(diào)用就可以了肴甸。

在三種主流的編程范式中寂殉,結(jié)構(gòu)化編程離圖靈機(jī)模型最近。人們學(xué)習(xí)編程的時(shí)候原在,大多數(shù)都是從結(jié)構(gòu)化編程開(kāi)始友扰。按照結(jié)構(gòu)化編程在做設(shè)計(jì)時(shí),也是按照指令和狀態(tài)(數(shù)據(jù))兩個(gè)緯度來(lái)考慮庶柿。在指令方面村怪,先分解過(guò)程 Procedure,然后通過(guò) Procedure 之間的一系列關(guān)系來(lái)構(gòu)建整個(gè)計(jì)算澳泵,對(duì)應(yīng)算法(流程圖)設(shè)計(jì)实愚。在狀態(tài)方面,將實(shí)例數(shù)據(jù)都以全局變量的形式放在模塊的靜態(tài)數(shù)據(jù)區(qū)兔辅,對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)腊敲。

procedure.png

架構(gòu)風(fēng)格

結(jié)構(gòu)化編程一般偏底層,一般適用于追求確定性和性能的系統(tǒng)軟件维苔。這類軟件偏靜態(tài)規(guī)劃碰辅,需求變化也不頻繁,適合多人并行協(xié)作開(kāi)發(fā)介时。將軟件先分完層和模塊没宾,然后再確定模塊間的 API,接著各組就可以同時(shí)啟動(dòng)開(kāi)發(fā)沸柔。各組進(jìn)行數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)和算法流程設(shè)計(jì)循衰,并在規(guī)定的時(shí)間內(nèi)進(jìn)行集成交付。分層模塊化架構(gòu)支撐了軟件的大規(guī)模并行開(kāi)發(fā)褐澎,且偏靜態(tài)規(guī)劃式開(kāi)發(fā)交付会钝。層與層之間限定了依賴方向,即層只能向下依賴,但同層內(nèi)模塊之間的依賴卻無(wú)法約束迁酸,經(jīng)常會(huì)出現(xiàn)模塊之間互相依賴的情況先鱼,導(dǎo)致可裁剪性和可復(fù)用性過(guò)粗,響應(yīng)變化能力較弱奸鬓。

s-module-style.png

結(jié)構(gòu)化編程的優(yōu)點(diǎn):

  • 貼近圖靈機(jī)模型焙畔,可以充分調(diào)動(dòng)硬件,控制性強(qiáng)串远。從硬件到 OS宏多,都是從圖靈機(jī)模型層累上來(lái)的。結(jié)構(gòu)化編程離硬圖靈機(jī)模型比較近澡罚,可以充分挖掘底下的能力绷落,盡量變得可控。
  • 流程清晰始苇。從 main 函數(shù)看代碼,可以一路看下去筐喳,直到結(jié)束催式。

結(jié)構(gòu)化編程的缺點(diǎn):

  • 數(shù)據(jù)的全局訪問(wèn)性帶來(lái)較高的耦合復(fù)雜度,局部可復(fù)用性及響應(yīng)變化能力差避归,模塊可測(cè)試性差荣月。想單獨(dú)復(fù)用一個(gè) Procedure 比較困難,需要將該過(guò)程函數(shù)相關(guān)的全局?jǐn)?shù)據(jù)及與全局?jǐn)?shù)據(jù)相關(guān)的其他過(guò)程函數(shù)(生命周期關(guān)聯(lián))及其他數(shù)據(jù)(指針變量關(guān)聯(lián))一起拎出來(lái)復(fù)用梳毙,但這個(gè)過(guò)程是隱式的哺窄,必須追著代碼一點(diǎn)點(diǎn)看才能做到。同理账锹,想要單獨(dú)修改一個(gè) Procedure 也比較困難萌业,經(jīng)常需要將關(guān)聯(lián)的所有Procedure 進(jìn)行同步修改才能做到,即散彈式修改奸柬。還有一點(diǎn)生年,就是模塊之間可能有數(shù)據(jù)耦合,打樁復(fù)雜度高廓奕,很難單獨(dú)測(cè)試抱婉。
  • 隨著軟件規(guī)模的不斷膨脹,結(jié)構(gòu)化編程組織程序的方式顯得比較僵硬桌粉。結(jié)構(gòu)化編程貼近圖靈機(jī)模型蒸绩,恰恰說(shuō)明結(jié)構(gòu)化編程抽象能力差,離領(lǐng)域問(wèn)題的距離比較遠(yuǎn)铃肯,在代碼中找不到領(lǐng)域概念的直接映射患亿,難以組織管理大規(guī)模軟件。

剛才在優(yōu)點(diǎn)中提到缘薛,結(jié)構(gòu)化編程貼近圖靈機(jī)模型窍育,可以充分調(diào)動(dòng)硬件卡睦,控制性強(qiáng)。為什么我們需要這個(gè)控制性漱抓?你可能做過(guò)嵌入式系統(tǒng)的性能優(yōu)化表锻,你肯定知道控制性是多么重要。你可能要優(yōu)化版本的二進(jìn)制大小乞娄,也可能要優(yōu)化版本的內(nèi)存占用瞬逊,還有可能要優(yōu)化版本的運(yùn)行時(shí)效率,這時(shí)你如果站在硬件怎么運(yùn)行的最佳狀態(tài)來(lái)思考優(yōu)化方法仪或,那么與圖靈機(jī)模型的 gap 就非常小确镊,則很容易找到較好的優(yōu)化方法來(lái)實(shí)施較強(qiáng)的控制性,否則中間有很多抽象層范删,則很難找到較好的優(yōu)化方法蕾域。

除過(guò)性能,確定性對(duì)于系統(tǒng)軟件來(lái)說(shuō)也很重要到旦。對(duì)于 5G旨巷,系統(tǒng)要求端到端時(shí)延不超過(guò) 1ms,我們不能 80% 的情況時(shí)延是 0.5ms添忘,而 20% 的情況時(shí)延卻是 2ms采呐。賣出一個(gè)硬件,給客戶承諾可以支持 2000 用戶搁骑,我們不能 80% 的情況可以支持 3000 用戶斧吐,而 20% 的情況僅支持 1000 用戶。靜態(tài)規(guī)劃性在某些系統(tǒng)軟件中是極度追求的仲器,這種確定性需要對(duì)底層的圖靈機(jī)模型做很好的靜態(tài)分解煤率,然后把我們的程序從內(nèi)存到指令和數(shù)據(jù)一點(diǎn)點(diǎn)映射下去。因?yàn)榻Y(jié)構(gòu)化編程離圖靈機(jī)模型較近乏冀,所以映射的 gap 比較小涕侈,容易通過(guò)靜態(tài)規(guī)劃達(dá)成這種確定性。

面向?qū)ο缶幊?/h2>

隨著軟件種類的不斷增多煤辨,軟件規(guī)模的不斷膨脹裳涛,人們希望可以更小粒度的對(duì)軟件進(jìn)行復(fù)用和裁剪。

基本設(shè)計(jì)

將全局?jǐn)?shù)據(jù)拆開(kāi)众辨,并將數(shù)據(jù)與其緊密耦合的方法放在一個(gè)邏輯邊界內(nèi)端三,這個(gè)邏輯邊界就是對(duì)象。用戶只能訪問(wèn)對(duì)象的 public 方法鹃彻,而看不到對(duì)象內(nèi)部的數(shù)據(jù)郊闯。對(duì)象將數(shù)據(jù)和方法天然的封裝在一個(gè)邏輯邊界內(nèi),可以整體直接復(fù)用而不用做任何裁剪或隱式關(guān)聯(lián)。

object.png

人們將領(lǐng)域問(wèn)題又開(kāi)始映射成實(shí)體及關(guān)系(程序 = 實(shí)體 + 關(guān)系)团赁,而不再是數(shù)據(jù)結(jié)構(gòu)和算法(過(guò)程)了育拨,這就是面向?qū)ο缶幊蹋诵奶攸c(diǎn)是封裝欢摄、繼承和多態(tài)熬丧。

封裝是面向?qū)ο蟮母鼘⒕o密相關(guān)的信息放在一起怀挠,形成一個(gè)邏輯單元析蝴。我們要隱藏?cái)?shù)據(jù),基于行為進(jìn)行封裝绿淋,最小化接口闷畸,不要暴露實(shí)現(xiàn)細(xì)節(jié)。

繼承分為兩種吞滞,即實(shí)現(xiàn)繼承和接口繼承佑菩。實(shí)現(xiàn)繼承是站在子類的視角看問(wèn)題,而接口繼承是站在父類的視角看問(wèn)題裁赠。很多程序員把實(shí)現(xiàn)繼承當(dāng)作一種代碼復(fù)用的方式倘待,但這并不是一種好的代碼復(fù)用方式,推薦使用組合组贺。

對(duì)于面向?qū)ο蠖裕鄳B(tài)至關(guān)重要祖娘,接口繼承是常見(jiàn)的一種多態(tài)的實(shí)現(xiàn)方式失尖。正因?yàn)槎鄳B(tài)的存在,軟件設(shè)計(jì)才有了更大的彈性渐苏,能夠更好地適應(yīng)未來(lái)的變化掀潮。只使用封裝和繼承的編程方式,我們稱之為基于對(duì)象編程琼富,而只有把多態(tài)加進(jìn)來(lái)仪吧,才能稱之為面向?qū)ο缶幊獭鞠眉?梢赃@么說(shuō)薯鼠,面向?qū)ο笤O(shè)計(jì)的核心就是多態(tài)的設(shè)計(jì)。

面向?qū)ο蠼?/h3>

面向?qū)ο缶幊陶Q生后械蹋,程序員需要從領(lǐng)域問(wèn)題映射到實(shí)體和關(guān)系這種模型出皇,后續(xù)再映射到圖靈機(jī)模型就交給面向?qū)ο缶幊陶Z(yǔ)言的編譯器來(lái)完成。于是問(wèn)題來(lái)了哗戈,領(lǐng)域千差萬(wàn)別郊艘,如何能將領(lǐng)域問(wèn)題高效簡(jiǎn)潔的映射到實(shí)體和關(guān)系?這時(shí) UML(Unified Model Language,統(tǒng)一建模語(yǔ)言)應(yīng)運(yùn)而生纱注,是由一整套圖表組成的標(biāo)準(zhǔn)化建模語(yǔ)言畏浆。可見(jiàn)狞贱,面向?qū)ο髽O大的推進(jìn)了軟件建模的發(fā)展刻获。

uml-view.png

現(xiàn)在有一些新的程序員對(duì)于 UML 不太熟悉,建議至少要掌握兩個(gè)UML圖斥滤,即類圖和序列圖:

  • 類圖是靜態(tài)視圖将鸵,體現(xiàn)類和結(jié)構(gòu)
  • 序列圖是動(dòng)態(tài)視圖,體現(xiàn)對(duì)象和交互

軟件設(shè)計(jì)一般從動(dòng)態(tài)圖開(kāi)始佑颇,在動(dòng)態(tài)交互中會(huì)把相對(duì)比較固定的模式下沉到靜態(tài)視圖里顶掉,然后形成類和結(jié)構(gòu)。在看代碼的時(shí)候挑胸,通過(guò)類和結(jié)構(gòu)就知道一部分對(duì)象和交互的信息了痒筒,可以約束及校驗(yàn)對(duì)象和交互的關(guān)系。

面向?qū)ο蠼R话惴譃樗膫€(gè)步驟:

  • 需求分析建模
  • 面向?qū)ο蠓治觯∣OA)
  • 面向?qū)ο笤O(shè)計(jì)(OOD)
  • 面向?qū)ο缶幋a(OOP)

在 OOA 階段茬贵,分析師產(chǎn)出分析模型簿透。同理,在 OOD 階段解藻,設(shè)計(jì)師產(chǎn)出設(shè)計(jì)模型老充。


tranditional-method.png

分析模型和設(shè)計(jì)模型的分離,會(huì)導(dǎo)致分析師頭腦中的業(yè)務(wù)模型和設(shè)計(jì)師頭腦中的業(yè)務(wù)模型不一致螟左,通常要映射一下啡浊。伴隨著重構(gòu)和 fix bug 的進(jìn)行,設(shè)計(jì)模型不斷演進(jìn)胶背,和分析模型的差異越來(lái)越大巷嚣。有些時(shí)候,分析師站在分析模型的角度認(rèn)為某個(gè)需求較容易實(shí)現(xiàn)钳吟,而設(shè)計(jì)師站在設(shè)計(jì)模型的角度認(rèn)為該需求較難實(shí)現(xiàn)廷粒,那么雙方都很難理解對(duì)方的模型。長(zhǎng)此以往红且,在分析模型和設(shè)計(jì)模型之間就會(huì)存在致命的隔閡坝茎,從任何活動(dòng)中獲得的知識(shí)都無(wú)法提供給另一方。

Eric Evans 在 2004 年出版了 DDD(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì), Domain-Driven Design)的開(kāi)山之作《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)——軟件核心復(fù)雜性應(yīng)對(duì)之道》暇番,拋棄將分析模型與設(shè)計(jì)模型分離的做法景东,尋找單個(gè)模型來(lái)滿足兩方面的要求,這就是領(lǐng)域模型奔誓。許多系統(tǒng)的真正復(fù)雜之處不在于技術(shù)斤吐,而在于領(lǐng)域本身搔涝,在于業(yè)務(wù)用戶及其執(zhí)行的業(yè)務(wù)活動(dòng)。如果在設(shè)計(jì)時(shí)沒(méi)有獲得對(duì)領(lǐng)域的深刻理解和措,沒(méi)有將復(fù)雜的領(lǐng)域邏輯以模型的形式清晰地表達(dá)出來(lái)庄呈,那么無(wú)論我們使用多么先進(jìn)多么流行的平臺(tái)和基礎(chǔ)設(shè)施,都難以保證項(xiàng)目的真正成功派阱。

DDD 是對(duì)面向?qū)ο蠼5难葸M(jìn)诬留,核心是建立正確的領(lǐng)域模型:


domain-model.png

DDD 的精髓是對(duì)邊界的劃分和控制,共有四重邊界:

  • 第一重邊界是在問(wèn)題空間分離子域贫母,包括核心域文兑,支撐域和通用域
  • 第二重邊界是在解決方案空間拆分 BC(限界上下文,Bounded Context)腺劣,BC 之間的協(xié)作關(guān)系通過(guò) Context Mapping(上下文映射) 來(lái)表達(dá)
  • 第三重邊界是在 BC 內(nèi)部分離業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度绿贞,形成分層架構(gòu),包括用戶界面層橘原,應(yīng)用層籍铁,領(lǐng)域?qū)雍突A(chǔ)設(shè)施層
  • 第四重邊界是在領(lǐng)域?qū)右刖酆线@一最小的設(shè)計(jì)單元,它從完整性與一致性對(duì)領(lǐng)域模型進(jìn)行了有效的隔離趾断,聚合內(nèi)部包括實(shí)體拒名、值對(duì)象、領(lǐng)域服務(wù)芋酌、工廠和倉(cāng)儲(chǔ)等設(shè)計(jì)元素

設(shè)計(jì)原則與模式

設(shè)計(jì)原則很多增显,程序員最常使用的是 SOLID 原則,它是一套比較成體系的設(shè)計(jì)原則脐帝。它不僅可以指導(dǎo)我們?cè)O(shè)計(jì)模塊(類)同云,還可以被當(dāng)作一把尺子,來(lái)衡量我們?cè)O(shè)計(jì)的有效性腮恩。

SOLID 原則是五個(gè)設(shè)計(jì)原則首字母的縮寫(xiě),它們分別是:

  • 單一職責(zé)原則(Single responsibility principle温兼,SRP):一個(gè)類應(yīng)該有且僅有一個(gè)變化的原因
  • 開(kāi)放封閉原則(Open–closed principle秸滴,OCP):軟件實(shí)體(類、模塊募判、函數(shù))應(yīng)該對(duì)擴(kuò)展開(kāi)放荡含,對(duì)修改封閉
  • 里氏替換原則(Liskov substitution principle,LSP):子類型(subtype)必須能夠替換其父類型(base type)
  • 接口隔離原則(Interface segregation principle届垫,ISP):不應(yīng)強(qiáng)迫使用者依賴于它們不用的方法
  • 依賴倒置原則(Dependency inversion principle释液,DIP):高層模塊不應(yīng)依賴于低層模塊,二者應(yīng)依賴于抽象装处;抽象不應(yīng)依賴于細(xì)節(jié)误债,細(xì)節(jié)應(yīng)依賴于抽象

前面我們提到浸船,對(duì)于面向?qū)ο髞?lái)說(shuō),核心是多態(tài)的設(shè)計(jì)寝蹈,我們看看 SOLID 原則如何指導(dǎo)多態(tài)設(shè)計(jì):

  • 單一職責(zé)原則:通過(guò)接口分離變與不變李命,隔離變化
  • 開(kāi)放封閉原則:多態(tài)的目標(biāo)是系統(tǒng)對(duì)于變化的擴(kuò)展而非修改
  • 里氏替換原則:接口設(shè)計(jì)要達(dá)到細(xì)節(jié)隱藏的圓滿效果
  • 接口隔離原則:面向不同客戶的接口要分離開(kāi)
  • 依賴倒置原則:接口的設(shè)計(jì)和規(guī)定者應(yīng)該是接口的使用方

除過(guò)設(shè)計(jì)原則,我們還要掌握常用的設(shè)計(jì)模式箫老。設(shè)計(jì)模式是針對(duì)一些普遍存在的問(wèn)題給出的特定解決方案封字,使面向?qū)ο蟮脑O(shè)計(jì)更加靈活和優(yōu)雅,從而復(fù)用性更好耍鬓。學(xué)習(xí)設(shè)計(jì)模式不僅僅要學(xué)習(xí)代碼怎么寫(xiě)阔籽,更重要的是要了解模式的應(yīng)用場(chǎng)景。不論那種設(shè)計(jì)模式牲蜀,其背后都隱藏著一些“永恒的真理”笆制,這個(gè)真理就是設(shè)計(jì)原則。的確各薇,還有什么比原則更重要呢项贺?就像人的世界觀和人生觀一樣,那才是支配你一切行為的根本峭判】校可以說(shuō),設(shè)計(jì)原則是設(shè)計(jì)模式的靈魂林螃。

守破離是武術(shù)中一種漸進(jìn)的學(xué)習(xí)方法:

  • 第一步——守奕删,遵守規(guī)則直到充分理解規(guī)則并將其視為習(xí)慣性的事
  • 第二步——破,對(duì)規(guī)則進(jìn)行反思疗认,尋找規(guī)則的例外并“打破”規(guī)則
  • 第三步——離完残,在精通規(guī)則之后就會(huì)基本脫離規(guī)則,抓住其精髓和深層能量

設(shè)計(jì)模式的學(xué)習(xí)也是一個(gè)守破離的過(guò)程:

  • 第一步——守横漏,在設(shè)計(jì)和應(yīng)用中模仿既有設(shè)計(jì)模式谨设,在模仿中要學(xué)會(huì)思考
  • 第二步——破,熟練使用基本設(shè)計(jì)模式后缎浇,創(chuàng)造新的設(shè)計(jì)模式
  • 第三步——離扎拣,忘記所有設(shè)計(jì)模式,在設(shè)計(jì)中潛移默化的使用

架構(gòu)風(fēng)格

面向?qū)ο笤O(shè)計(jì)大行其道以后素跺,組件化或服務(wù)化架構(gòu)風(fēng)格開(kāi)始流行起來(lái)二蓝。組件化或服務(wù)化架構(gòu)風(fēng)格參考了對(duì)象設(shè)計(jì):對(duì)象有生命周期,是一個(gè)邏輯邊界指厌,對(duì)外提供 API刊愚;組件或服務(wù)也有生命周期,也是一個(gè)邏輯邊界踩验,也對(duì)外提供 API鸥诽。在這種架構(gòu)中商玫,應(yīng)用依賴導(dǎo)致原則,不論高層還是低層都依賴于抽象衙传,好像整個(gè)分層架構(gòu)被推平了决帖,沒(méi)有了上下層的關(guān)系。不同的客戶通過(guò)“平等”的方式與系統(tǒng)交互蓖捶,需要新的客戶嗎地回?不是問(wèn)題,只需要添加一個(gè)新的適配器將客戶輸入轉(zhuǎn)化成能被系統(tǒng) API 所理解的參數(shù)就行俊鱼。同時(shí)刻像,對(duì)于每種特定的輸出,都有一個(gè)新建的適配器負(fù)責(zé)完成相應(yīng)的轉(zhuǎn)化功能并闲。

port-adapter.png

面向?qū)ο缶幊痰膬?yōu)點(diǎn):

  • 對(duì)象自封裝數(shù)據(jù)和行為细睡,利于理解和復(fù)用
  • 對(duì)象作為“穩(wěn)定的設(shè)計(jì)質(zhì)料”帝火,適合廣域使用溜徙。
  • 多態(tài)提高了響應(yīng)變化的能力,進(jìn)一步提升了軟件規(guī)模犀填。
  • 對(duì)設(shè)計(jì)的理解和演進(jìn)優(yōu)先是對(duì)模型和結(jié)構(gòu)的理解和調(diào)整蠢壹。不要一上來(lái)就看代碼,面向?qū)ο蟮拇a看著看著很容易斷九巡,比如遇到虛接口图贸,就跟不下去了。通常是先掌握模型和結(jié)構(gòu)冕广,然后在結(jié)構(gòu)中打開(kāi)某個(gè)點(diǎn)的代碼進(jìn)行查看和修改疏日。請(qǐng)記住,先模型撒汉,再接口沟优,后實(shí)現(xiàn)。

面向?qū)ο缶幊痰娜秉c(diǎn):

  • 業(yè)務(wù)邏輯碎片化睬辐,散落在離散的對(duì)象內(nèi)挠阁。類的設(shè)計(jì)遵循單一職責(zé)原則,為了完成一個(gè)業(yè)務(wù)流程溉委,需要在多個(gè)類中跳來(lái)跳去鹃唯。
  • 行為和數(shù)據(jù)的不匹配協(xié)調(diào)爱榕,即所謂的貧血模型和充血模型之爭(zhēng)瓣喊。后來(lái)發(fā)現(xiàn)可通過(guò) DCI(Data、Context 和 Interactive)架構(gòu)來(lái)解決該問(wèn)題黔酥。
  • 面向?qū)ο蠼R蕾嚬こ探?jīng)驗(yàn)藻三,缺乏嚴(yán)格的理論支撐洪橘。面向?qū)ο蠼;卮鹆藦念I(lǐng)域問(wèn)題如何映射到對(duì)象模型棵帽,但一般只是講 OOA 和 OOD 的典型案例或最佳實(shí)踐熄求,屬于歸納法范疇,并沒(méi)有嚴(yán)格的數(shù)學(xué)推導(dǎo)和證明逗概。

函數(shù)式編程

與結(jié)構(gòu)化編程與面向?qū)ο缶幊滩煌芡恚瘮?shù)式編程對(duì)很多人來(lái)說(shuō)要陌生一些。你可能知道逾苫,C++ 和 Java 已經(jīng)引入了 Lambda 表達(dá)式卿城,目的就是為了支持函數(shù)式編程。函數(shù)式編程中的函數(shù)不是結(jié)構(gòu)化編程中的函數(shù)铅搓,而是數(shù)學(xué)中的函數(shù)瑟押,結(jié)構(gòu)化編程中的函數(shù)是一個(gè)過(guò)程(Procedure)。

基本設(shè)計(jì)

函數(shù)式編程的起源是數(shù)學(xué)家 Alonzo Church 發(fā)明的 Lambda 演算(Lambda calculus星掰,也寫(xiě)作 λ-calculus)多望。所以,Lambda 這個(gè)詞在函數(shù)式編程中經(jīng)常出現(xiàn)氢烘,你可以把它簡(jiǎn)單地理解成匿名函數(shù)怀偷。

functions.png

函數(shù)式編程有很多特點(diǎn):

  • 函數(shù)是一等公民。一等公民的含義:(1)它可以按需創(chuàng)建威始;(2)它可以存儲(chǔ)在數(shù)據(jù)結(jié)構(gòu)中枢纠;(3)它可以當(dāng)作參數(shù)傳給另一個(gè)函數(shù);(4)它可以當(dāng)作另一個(gè)函數(shù)的返回值黎棠。
  • 純函數(shù)晋渺。所謂純函數(shù),是符合下面兩點(diǎn)的函數(shù):(1)對(duì)于相同的輸入脓斩,返回相同的輸出木西;(2)沒(méi)有副作用。
  • 惰性求值随静。惰性求值是一種求值策略八千,它將求值的過(guò)程延遲到真正需要這個(gè)值的時(shí)候。
  • 不可變數(shù)據(jù)燎猛。函數(shù)式編程的不變性主要體現(xiàn)在值和純函數(shù)上恋捆。值類似于 DDD 中的值對(duì)象,一旦創(chuàng)建重绷,就不能修改沸停,除非重新創(chuàng)建。值保證不會(huì)顯式修改一個(gè)數(shù)據(jù)昭卓,純函數(shù)保證不會(huì)隱式修改一個(gè)數(shù)據(jù)愤钾。當(dāng)你深入學(xué)習(xí)函數(shù)式編程時(shí)瘟滨,會(huì)遇到無(wú)副作用、無(wú)狀態(tài)和引用透明等說(shuō)法能颁,其實(shí)都是在討論不變性杂瘸。
  • 遞歸。函數(shù)式編程用遞歸作為流程控制的機(jī)制伙菊,一般為尾遞歸败玉。

函數(shù)式編程還有兩個(gè)重要概念:高階函數(shù)和閉包。所謂高階函數(shù)镜硕,是指一種比較特殊的函數(shù)绒怨,它們可以接收函數(shù)作為輸入,或者返回一個(gè)函數(shù)作為輸出谦疾。閉包是由函數(shù)及其相關(guān)的引用環(huán)境組合而成的實(shí)體南蹂,即閉包 = 函數(shù) + 引用環(huán)境

閉包有獨(dú)立生命周期念恍,能捕獲上下文(環(huán)境)六剥。站在面向?qū)ο缶幊痰慕嵌龋]包就是只有一個(gè)接口(方法)的對(duì)象峰伙,即將單一職責(zé)原則做到了極致疗疟。可見(jiàn)瞳氓,閉包的設(shè)計(jì)粒度更小策彤,創(chuàng)建成本更低,很容易做組合式設(shè)計(jì)匣摘。在面向?qū)ο缶幊讨械晔O(shè)計(jì)粒度是一個(gè) Object,它可能還需要拆音榜,但你可能已經(jīng)沒(méi)有意識(shí)再去拆庞瘸,那么上帝類大對(duì)象就會(huì)存在了,創(chuàng)建成本高赠叼。在函數(shù)式編程中擦囊,閉包給你一個(gè)更精細(xì)化設(shè)計(jì)的能力,一次就可以設(shè)計(jì)出單一接口的有獨(dú)立生命周期的可以捕獲上下文的原子對(duì)象嘴办,天然就是易于組合易于重用的瞬场,并且是易于應(yīng)對(duì)變化的。

有一句化說(shuō)的很好:閉包是窮人的對(duì)象涧郊,對(duì)象是窮人的閉包贯被。有的語(yǔ)言沒(méi)有閉包,你沒(méi)有辦法,只能拿對(duì)象去模擬閉包刃榨。又有一些語(yǔ)言沒(méi)有對(duì)象,但單一接口不能完整表達(dá)一個(gè)業(yè)務(wù)概念双仍,你沒(méi)有辦法枢希,只能將多個(gè)閉包組合在一起當(dāng)作對(duì)象用。

對(duì)于函數(shù)式編程朱沃,數(shù)據(jù)是不可變的苞轿,所以一般只能通過(guò)模式匹配和遞歸來(lái)完成圖靈計(jì)算。當(dāng)程序員選擇將函數(shù)式編程作為思維底座時(shí)逗物,就需要解決如何將領(lǐng)域問(wèn)題映射到數(shù)據(jù)和函數(shù)(程序 = 數(shù)據(jù) + 函數(shù))搬卒。

函數(shù)式設(shè)計(jì)的思路就是高階函數(shù)與組合,背后是抽象代數(shù)那一套邏輯翎卓。下面這張圖是關(guān)于高階函數(shù)的契邀,左邊是將函數(shù)作為輸入,右邊是將函數(shù)作為輸出:


high-order-function.png

對(duì)于將函數(shù)作為輸入的高階函數(shù)失暴,就是面向?qū)ο蟮牟呗阅J脚髅拧?duì)于將函數(shù)作為輸出的高階函數(shù),就是面向?qū)ο蟮墓S模式逗扒。每個(gè)高階函數(shù)都是職責(zé)單一的古戴,所以函數(shù)式設(shè)計(jì)是以原子的方式通過(guò)策略模式和工廠模式來(lái)組合類似面向?qū)ο蟮囊磺小T谶@個(gè)過(guò)程中矩肩,到底哪些函數(shù)作為入?yún)⑾帜眨男┖瘮?shù)作為返回值,然后這些返回值函數(shù)再傳給哪些函數(shù)黍檩,接著再返回哪些函數(shù)......舷丹,你發(fā)現(xiàn)你在套公式,通過(guò)公式的層層嵌套完成一個(gè)算法的描述攻谁,所以核心就是設(shè)計(jì)有哪些高階函數(shù)以及它們的組合規(guī)則断盛,這是函數(shù)式設(shè)計(jì)中最難的,就是抽象代數(shù)的部分肛跌∫张洌可見(jiàn),函數(shù)式設(shè)計(jì)的基本方法為:借助閉包的單一接口的標(biāo)準(zhǔn)化和高階函數(shù)的可組合性衍慎,通過(guò)規(guī)則串聯(lián)設(shè)計(jì)转唉,完成數(shù)據(jù)從源到結(jié)果的映射描述。這里的映射是通過(guò)多個(gè)高階函數(shù)的形式化組合完成稳捆,描述就像寫(xiě)數(shù)學(xué)公式一樣放在那赠法,等源數(shù)據(jù)從一頭傳入,然后經(jīng)過(guò)層層函數(shù)公式的處理,最后變成你想要的結(jié)果砖织。數(shù)據(jù)在形式化轉(zhuǎn)移的過(guò)程中款侵,不僅僅包括數(shù)據(jù)本身,還包括規(guī)則的創(chuàng)建侧纯、返回和傳遞新锈。

架構(gòu)風(fēng)格

前面我們講過(guò),函數(shù)式編程引起人們重視的因素包括硬件性能提升眶熬,多核 CPU 和分布式計(jì)算等妹笆。函數(shù)式編程的一些特點(diǎn),使得并發(fā)程序更容易寫(xiě)了娜氏。一些架構(gòu)風(fēng)格拳缠,尤其是分布式系統(tǒng)的架構(gòu)風(fēng)格,借鑒了函數(shù)式的特點(diǎn)贸弥,使得系統(tǒng)的擴(kuò)展性和彈性變得更容易窟坐。

函數(shù)式編程的建模方式是抽象代數(shù),在上面層累出兩類架構(gòu)風(fēng)格:
(1)Event Sourcing绵疲,Reative Achitecture


event-sourcing.png

(2)Lambda Achitecture狸涌,F(xiàn)aaS,Serverless


lambda.png

借鑒函數(shù)式編程的理念最岗,分布式系統(tǒng)的架構(gòu)風(fēng)格帕胆,在架構(gòu)層面完成更高抽象力度的表達(dá),在并發(fā)層面完成更好的彈性和可靠性般渡。

函數(shù)式編程的優(yōu)點(diǎn):

  • 高度的抽象懒豹,易于擴(kuò)展。函數(shù)式編程是數(shù)據(jù)化表達(dá)驯用,非常抽象脸秽,在表達(dá)范圍內(nèi)是易于擴(kuò)展的。
  • 聲明式表達(dá)蝴乔,易于理解记餐。
  • 形式化驗(yàn)證,易于自證薇正。
  • 不可變狀態(tài)片酝,易于并發(fā)。數(shù)據(jù)不可變不是并發(fā)的必要條件挖腰,不共享數(shù)據(jù)才是雕沿,但不可變使得并發(fā)更加容易。

函數(shù)式編程的缺點(diǎn):

  • 對(duì)問(wèn)題域的代數(shù)化建模門檻高猴仑,適用域受限∩舐郑現(xiàn)實(shí)是復(fù)雜的,不是在每個(gè)方面都是自洽的,要找到一套完整的規(guī)則映射是非常困難的疾渣。在一些狹窄的領(lǐng)域篡诽,可能找得到,而一旦擴(kuò)展一下榴捡,就會(huì)破壞該狹窄領(lǐng)域杈女,你發(fā)現(xiàn)以前找到的抽象代數(shù)建模方式就不再適用了。
  • 在圖靈機(jī)上性能較差薄疚。函數(shù)式編程增加了很多中間層,它的規(guī)則描述和惰性求值等使得優(yōu)化變得困難赊琳。
  • 不可變的約束造成了數(shù)據(jù)泥團(tuán)耦合街夭。領(lǐng)域?qū)ο笫怯袪顟B(tài)的,這些狀態(tài)只能通過(guò)函數(shù)來(lái)傳遞躏筏,導(dǎo)致很多函數(shù)有相同的入?yún)⒑头祷刂怠?/li>
  • 閉包接口粒度過(guò)細(xì)板丽,往往需要再組合才能構(gòu)成業(yè)務(wù)概念

小結(jié)

作為一個(gè)程序員趁尼,我們應(yīng)該清楚每種編程范式的適用場(chǎng)景埃碱,在特定的場(chǎng)景下選擇合適的范式來(lái)恰當(dāng)?shù)慕鉀Q問(wèn)題。

多范式融合的設(shè)計(jì)建議:

  • 每種編程范式都有優(yōu)缺點(diǎn)酥泞,不做某單一范式的擁坌砚殿,分場(chǎng)景靈活選擇合適的范式恰當(dāng)?shù)慕鉀Q問(wèn)題
  • 從 DDD 的角度,按照模型一致性芝囤,將不同范式的設(shè)計(jì)劃分到不同的子域似炎、BC 或?qū)觾?nèi)

最后,我們重新看看開(kāi)始的那張編程范式之間的關(guān)系圖:


relations.png

說(shuō)明如下:

  • 最早是非結(jié)構(gòu)化編程悯姊,指令可以隨便跳羡藐,數(shù)據(jù)可以隨便引用。后來(lái)有了結(jié)構(gòu)化編程悯许,人們把 goto 語(yǔ)句去掉了仆嗦,約束了指令的方向性,過(guò)程之間是單向的先壕,但數(shù)據(jù)卻是可以全局訪問(wèn)的瘩扼。再到面向?qū)ο缶幊痰臅r(shí)候,人們干脆將數(shù)據(jù)與其緊密耦合的方法放在一個(gè)邏輯邊界內(nèi)垃僚,約束了數(shù)據(jù)的作用域邢隧,靠關(guān)系來(lái)查找。最后到函數(shù)式編程的時(shí)候冈在,人們約束了數(shù)據(jù)的可變性倒慧,通過(guò)一系列函數(shù)的組合來(lái)描述數(shù)據(jù)從源到目標(biāo)的映射規(guī)則的編排,在中間它是無(wú)狀態(tài)的∪伊拢可見(jiàn)炫贤,從左邊到右邊,是一路約束的過(guò)程付秕。
  • 越往左邊限制越少兰珍,越貼近圖靈機(jī)模型,可以充分調(diào)動(dòng)硬件询吴,“直接”帶來(lái)的可控性及廣域適用性掠河。對(duì)于可控性,因?yàn)殡x圖靈機(jī)模型很近猛计,可以按自己的想法來(lái)“直接”控制唠摹。對(duì)于廣域適用性,因?yàn)榧s束越多奉瘤,說(shuō)明門檻越高勾拉,一旦右邊搞不定,可以往回退一步盗温,當(dāng)你找到合理的對(duì)象模型或抽象代數(shù)模型時(shí)藕赞,可以再往前走一步。
  • 越往右邊限制越多卖局,通過(guò)約束建立規(guī)則斧蜕,通過(guò)規(guī)則描述系統(tǒng),“抽象”帶來(lái)的定域擴(kuò)展性砚偶。對(duì)于定域惩激,因?yàn)檫@種“抽象”一定是面向某一個(gè)狹窄的切面,找到的對(duì)象模型或抽象代數(shù)模型會(huì)有很強(qiáng)的擴(kuò)展性和可理解性蟹演,但一旦超過(guò)這個(gè)范圍风钻,模型可能就無(wú)效了,所以 DDD 一直在強(qiáng)調(diào)分離子域酒请、劃分 BC 和分層架構(gòu)骡技。

參考資料

  • C++及系統(tǒng)軟件技術(shù)大會(huì)2020,《多范式融合的Modern C++軟件設(shè)計(jì)》羞反,王博
  • 極客時(shí)間專欄布朦,《軟件設(shè)計(jì)之美》,鄭曄
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昼窗,一起剝皮案震驚了整個(gè)濱河市是趴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌澄惊,老刑警劉巖唆途,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件富雅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡肛搬,警方通過(guò)查閱死者的電腦和手機(jī)没佑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)温赔,“玉大人蛤奢,你說(shuō)我怎么就攤上這事√赵簦” “怎么了啤贩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)拜秧。 經(jīng)常有香客問(wèn)我痹屹,道長(zhǎng),這世上最難降的妖魔是什么腹纳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任痢掠,我火速辦了婚禮驱犹,結(jié)果婚禮上嘲恍,老公的妹妹穿的比我還像新娘。我一直安慰自己雄驹,他們只是感情好佃牛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著医舆,像睡著了一般俘侠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔬将,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天爷速,我揣著相機(jī)與錄音,去河邊找鬼霞怀。 笑死惫东,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的毙石。 我是一名探鬼主播廉沮,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼徐矩!你這毒婦竟也來(lái)了滞时?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤滤灯,失蹤者是張志新(化名)和其女友劉穎坪稽,沒(méi)想到半個(gè)月后曼玩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刽漂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年演训,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贝咙。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡样悟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庭猩,到底是詐尸還是另有隱情窟她,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布蔼水,位于F島的核電站震糖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏趴腋。R本人自食惡果不足惜吊说,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望优炬。 院中可真熱鬧颁井,春花似錦、人聲如沸蠢护。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)葵硕。三九已至眉抬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間懈凹,已是汗流浹背蜀变。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留介评,地道東北人库北。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像威沫,于是被迫代替她去往敵國(guó)和親贤惯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • 編程范式 托馬斯.庫(kù)爾提出“科學(xué)的革命”的范式論后棒掠,Robert Floyd在1979年圖靈獎(jiǎng)的頒獎(jiǎng)演說(shuō)中使用了編...
    zhoulujun閱讀 291評(píng)論 0 1
  • 面向?qū)ο缶幊汤狻⒚嫦蜻^(guò)程編程、函數(shù)式編程等等恤筛,這些也許是大家經(jīng)常會(huì)聽(tīng)到的術(shù)語(yǔ)官还,這些都是什么意思,干什么用毒坛,他們之間有...
    在線工程師閱讀 1,309評(píng)論 0 1
  • 許多現(xiàn)存的編程語(yǔ)言都可基于其計(jì)算模型加以分類望伦,歸入某些語(yǔ)言族,或者屬于某種編程范式煎殷。按照不同的規(guī)則屯伞,可以有多種分類...
    Albert陳凱閱讀 1,245評(píng)論 0 2
  • 什么是編程范式 所謂編程范式(programming paradigm),指的是計(jì)算機(jī)編程的基本風(fēng)格或典范模式豪直。我...
    散落_a0b3閱讀 1,246評(píng)論 0 1
  • 計(jì)算機(jī)本身是從數(shù)學(xué)發(fā)展出來(lái)的劣摇,因此最早的編程人員反而都是數(shù)學(xué)家。但到了現(xiàn)在弓乙,我們不得不承認(rèn)末融,計(jì)算機(jī)的工程師們和數(shù)學(xué)...
    空即是色即是色即是空閱讀 1,068評(píng)論 0 2