程式設計師思維

venblenJan°發表於2024-04-14

程式設計師思維

起因

首先簡單說一下,為什麼我會想到這個話題。主要有這麼幾方面的原因。

當我試圖回過頭去總結大學在計算機專業所學習的一些理論和知識的時候。發現,在學校裡面學習的一些東西,走了兩個極端。 一個極端是偏向了細節。比如我們學習的那些《***程式設計》的課程。看這幾門課的名稱的我們能夠很明顯的看出,***是一個形容詞定語,用來修飾主題“程式設計”。但是,你卻非常意外的意識到《C++物件導向程式設計》和麵向物件程式設計貌似關係不大,整門課程主要講了一個更好用的C,比C好用的地方是在於人家有物件。學習了這一系列《***程式設計》的課程之後的結果是:你知道了彙編的語法,知道了C的語法,知道了C++的語法;但是用他們能做些什麼卻不知道。 另外一個極端是偏向了巨集觀。在我們對工程是個什麼東西,專案是個什麼東西,軟體又是個什麼東東還沒有構建起最基本概念的時候。我們上了《軟體工程》這樣的課程。劈頭蓋臉的理論砸下來了,沒有消化也沒有吸收。你甚至找不到,學這些課程對於你的程式設計實踐有什麼樣的版主。等你寫程式碼的時候,你真真切切的發現軟體工程這個東西對於我這個函式怎麼命名,模組怎麼劃分真的幫助不是很大啊。 其實,隨著時間一點點的流逝,工作年限的增加。你能夠發現,這兩個極端的東西還是有作用的。只不過,學校裡面的課程少了一些能夠把它們融會貫通,串聯起來的東西。而這個中間起到粘連作用的東西就是我所思考的。


另外一個原因是,當與其他同學或同事去寫同一個程式的時候。他們有些時候看到你的程式碼,然後就會評論:你這程式碼耦合性太緊,內聚性太差,不符合高內聚低耦合的概念啊。你當然,有點氣不過啊。你憑什麼說我程式碼沒高內聚低耦合啊。於是你就問他為什麼這麼說。結果絕大多數時候,對方只是說”我感覺“。即使有些時候他們根據他們多年的程式設計經驗說出了一些什麼東西。但是你還是感覺說服不了你。你心裡會暗自嘀咕,不就是你感覺嘛。你感覺的東西也不一定對。 同樣的問題,也會發生在當你去評論別人的程式碼質量差的時候。要想讓別人認識到問題所在是異常困難的。果然世界上最難的事情有兩個:把別人的錢放進自己的口袋裡面,把自己的思想放進別人的腦袋裡面。 於是,你就會去思考,有沒有一種理論或者評價的方法,是大家都認可的。而且的確能夠衡量出一個設計耦合性和內聚性的強弱來呢?


還有一個原因。和上面的原因有點類似,在程式設計中接觸到的很多同事。或者同樣是程式設計的人。我發現他們的能力參差不齊。而且,有些時候這種能力的參差不齊不止是因為經驗造成的。當然,一個有十年多程式設計經驗的人肯定比一個剛剛入門的小孩寫的程式靠譜。但是,你會驚奇的發現有些人寫了四五年程式後,程式的質量甚至比十幾年經驗的人要好很多。與他們聊天的時候,就發現導致這種現象的原因在於他們思維方式的不同。有很多十幾年程式設計經驗的同事,很多時候只是在用感覺程式設計。在長期程式設計時間中形成的某些思維定勢幫助他們能夠快速的完成編碼工作,但是僅限於此。而那些年少卻有能力的同事,雖然年輕但是能夠編寫高質量的程式碼。因為他們不是通過經驗積累獲得編碼能力;而是通過理論學習,並且快速的消化掌握了和十年多經驗同事一樣的思維定勢。不一樣的是,他們還能通過理論觸類旁通。寫出更高質量的程式碼。 以前有個同事(他不是幹程式設計師的,而是產品經理),他和其他同齡人最大的不同就是思維方式。他總喜歡幹一件事情就是找規律。後來,他想自己做點東西,一時又沒有拉到程式設計師一起幹,就自己操刀學習JS。你看他的程式碼,才一兩個月的時間,程式碼質量和以JS為職業的人已經有的一拼了。每當想到這,我就想:我們還能說對於程式設計這件事情經驗是最重要的嗎?


總結一下上面的原因就是,在我們的程式設計實踐中,我們需要找到一種思維框架來幫助我們設計和解釋我們的程式。這個東西就是要討論的程式設計師思維。

從哪裡來,是什麼

先講一個笑話,說是一個外國的哲學家來中國做客。中途去一個小區找一個朋友,然後就發現原來中國的小區保安都是哲學家。他進門的時候,保安問了哲學家三個問題: 你從哪裡來? 你是誰? 你到哪裡去? 哲學家就感慨這是終極問題啊,自己窮其一生也沒能解答,沒想到這麼個小地方還有人關心哲學的終極問題。其實人家保安就是想搞明白這個“哲學家”嘛。因為你要弄明白一個概念,只要能夠回答好這三個問題,基本上就比較OK了。同樣我們要弄明白“程式設計師思維”,也要問三個問題?

  1. 程式設計師思維是什麼?
  2. 程式設計師思維從哪裡來?
  3. 程式設計師思維到哪裡去?

好吧,這是三個終極的問題。沒有標準答案,也沒有什麼不標準的答案。我只是試圖給出自己的一個思考的總結。

類比法的一個描述

對於程式設計師思維這個東西現在還給不出一個大家都能夠接受的描述來定義它。當我們無法非常準確的定義一個東西的時候,我們回去找這個東西的類似的東西來描述它。就像字典裡面用同義詞來解釋某一個詞語一樣。我們先來看看程式設計師思維像個什麼東西。說的具體一點就是我們程式設計敲程式碼這個事情像是什麼? 這裡有不同的說法。 有些人說像是做數學證明題。你看到了一道數學題,然後就開始在你腦袋裡面蒐羅能夠用來證明這個題目的定理和方法。然後,按照一定的順序把這些定理和方法組織起來,一步接一步的,就證明了。而寫程式,有些時候就是產品來了一個需求,我們就開始在腦袋裡蒐羅能夠幫助我們實現這個需求的方法和工具,很多時候是一些演算法或者程式的組織形式,然後把這些東西一個字元一個字元的敲下來,不出意外的情況下,產品的需求就實現了。 有些人說,程式設計序就像是在操作機器。和司機了沒有多大的區別。你往左打方向盤,車就往左開;你往右打方向盤,車就往右開。你給機器下達一條進行加法的命令,機器即開始進行加法運算;你給機器下達一條進行減法的命令,機器就開始進行減法運算。 有些人說,程式設計或者程式設計這個東西。就像是作家寫作,都是在操作字元。都是敲敲打打的寫了一大堆字元,然後竟然還能夠通過這些字元賺錢。 有些人說,整個軟體的製作過程和蓋房子差不多。都有終端的使用者,一個是買房,一個是軟體使用者。有了需求然後就有了公司,房地產公司和軟體公司。公司就開始招人了。房地產公司找了建築設計師,軟體公司找了架構師。有了設計,就得動工啊。房地產公司找了包工頭把專案包給了報共同,軟體公司招了一批專案經理來負責控制進度。包工頭找了幾個能挑頭的熟練工,然後每個熟練工後面跟著一批農民工。專案經理開始給高階程式設計師佈置任務,每個高階程式設計師手下都有幾個碼農。然後開始沒日沒夜的幹活。最後把東西交付到終端使用者手上。 。。。。。 如果我們繼續這樣類比下去,可能這是個無窮無盡的大列表。我們們適可而止。 不知道通過上面的這些類別有沒有發現一些什麼東西。上面說的這些事情,都是在用工具來解決問題或者通過工具來達到一些目的。程式設計或者程式設計也沒有逃出這個框架,我們用程式這個工具來完成需求。 馬克思*韋伯在《新教理論與資本主義》中首次提出了一個概念能夠很好的解釋上面列舉的事情的共性——工具理性。所謂“工具理性”,就是通過實踐的途徑確認工具(手段)的有用性,從而追求事物的最大功效,為人的某種功利的實現服務。工具理性是通過精確計算功利的方法最有效達至目的的理性,是一種以工具崇拜和技術主義為生存目標的價值觀,所以“工具理性”又叫“功效理性”或者說“效率理性”。 仔細一想,程式設計師思維就是在工具理性這個大的思維框架下面。所以一,程式設計師思維勢必符合工具理性的一些特徵。這有點想父類的子類的關係。工具理性是父類,程式設計師思維是子類。既然如此我們要了解程式設計師思維,那麼我們先來看看工具理性。

工具理性

雖然工具理性是在19世紀提出來的一個概念。但是像絕大部分的概念和定理一樣,雖然我們沒有認知到它,但是它已經在那裡。哥白尼之前人們沒有認識到地球是圍繞著太陽轉的,但是太陽在那之前也是圍繞著太陽轉的。雖然我們在很長一段時間內沒有明確的提出工具理性的概念,但是我們確實一直在實踐著工具理性,而且是從一開始。 人從靈長類的猿開始進化的時候,與猿最大的不同就是我們開始使用工具。WIKI上關於工具的定義是這樣的:工具(英語:Tool)或裝備、器材(英語:Equipment)是指能夠方便人們完成工作的器具,它的好處可以是機械性,也可以是智慧性的。大部分工具都是簡單機械;例如一根鐵棍可以當作槓桿使用,力點離開支點越遠,槓桿傳遞的力就越大。 雖然絕大部分的篇幅是在描述機械性的工具,但是其中很短的一句“也可以是智慧性的”貌似說出了些什麼。有很多人類學家認為人與動物的最大卻在於使用工具,後來又有一部分人對這句話做出了修正:人與動物最大的卻別是在於能夠使用智力工具。試想一下,對於使用石頭這個工具來砸核桃這個事情來說,是這個工具石頭重要,還是知道用什麼樣的石頭,怎樣的區砸重要呢? 或許從砸核桃這個事情上,你就能夠看到後來我們稱之為“演算法”的東西的影子。

  1. 把核桃放在硬質的地上。
  2. 找到一塊非常堅硬的石頭。
  3. 抬高石頭,用力砸核桃。
  4. 監察核桃是否碎裂,如果碎裂執行5,沒有碎裂執行3
  5. 吃掉核桃

而在這個過程中,是哪塊石頭並不重要,重要的是它能夠幫助我們完成上面的步驟。真正的核心還是步驟。 古希臘有個叫泰勒斯的數學家和哲學家。生活窮困潦倒,竟然靠借貸度日。但是,他醉心研究哲學和數學。周圍的鄰居就嘲笑他,說他淨整些沒用的。思考的那些東西又不能當飯吃。剛開始,泰勒斯沒太當回事。後來周圍的人都以此為笑柄,甚至教育孩子不要學他。於是,在某一年的春季。那年的春天比較乾旱,人們都預計這年的橄欖的收成會很不好。但是,泰勒斯借錢,用極低的價格買斷了當地榨汁機的使用權。當秋天到來,結果橄欖豐收。但是榨汁機的使用權卻在泰勒斯手上,泰勒斯高價出售了榨汁機的使用權。大賺!原來泰勒斯,在很早之前就通過自己的思考和分析,預見到今年橄欖會豐收。漂亮的給了嘲笑他的人響亮的回擊。 這個過程中,橄欖重要嗎?榨油機重要嗎?都重要,但是沒有泰勒斯的思維重要。是他思維的工具幫助他賺到了這一筆錢。同樣思維方式或者思維工具起到決定性作用的例子數不勝數。比如以少勝多的赤壁之戰之類。 在工具這個層面理解的話,思維工具要比機械性的工具要重要的多。而仔細分析,其實機械性的工具只不過是把我們的思維工具固化成了實物——把我們對鋒利的認知、理解與分析物化成了刀子;把我們對色彩的認知理解分支物化成了筆;把我們對水流動性的認知物化成了杯子。。。。。。不一而足。的確思維工具要想對現實產生作用,必須經過物化這一層。思維工具只有變成一個實物了才能夠真正的對現實世界產生作用。於是機械性工具的作用,不過就是承載我們思維工具的嘛,不就是我們思維工具的物化嘛! 而程式呢,通過計算機這個機械性的工具物化了,然後又連結到了各式各樣的物化的工具上面。而神奇的是,我們在有了計算機之後創造工具的方式與以前有天壤之別,我們不需要直接去創造機械性的工具了。程式讓我們有了間接操作機械性工具的能力。我們只需要負責思考就好了。而這思考的內容是我們目前為止掌握的所有的知識。 程式這個東西實在物化我們現在幾乎所有能夠掌握的知識。我們通過程式協作,通過程式作圖,通過程式餵豬,通過程式通訊,通過程式定飯店。。。 於是程式設計就是利用計算機將知識物化,並利用知識的可復現性來對現實產生作用。程式設計師所要做的事情就是用理性的工具來完成這一過程。而程式設計師思維就是幫助我們來完成這個事情的那一整套的思維框架。

一些歷史

大家比較公認的世界上第一個程式設計師是個女程式設計師。阿達·洛芙萊斯(Ada Lovelace)的名字是否使你想起什麼呢?沒有?它應該在電腦、平板或手機上出現過。這位維多利亞時代的女士、三個孩子的母親出生於1815年,是世界上第一個計算機程式設計師。 他是查爾斯·巴貝齊(Charles Babbage)的朋友,巴貝奇發明了一個很奇怪的機器(可以把它看作是第一臺計算機)——差分機,洛芙萊斯用巴貝齊的數學機器翻譯義大利數學家路易吉·蒙博(Luigi Menabrea)裡的筆記。洛芙萊斯進行的很順利,用她自己豐富的數學知識擴充套件了蒙博裡的筆記,她用一種演算法使計算機能夠識別一系列數字,有效的發明了第一個計算機程式。 她最要的貢獻就是提出了類似於軟體或者程式的概念,將思維工具與具體的機械屬性剝離。在單一的機械工具上你能夠物化不同的思維工具。這就是程式的魅力所在,它讓思維工具減少了對機械的依賴。 她和巴貝奇所處的時代是工業革命爆發的時代,或者話句話說他們所處的時代是一個工具大爆炸的時代。通常認為人類生產力突飛猛進式的躍進有三個時期: 第一個是農業革命,我們發明了農業,將天文學、數學。。。。那個時候我們能夠掌握的絕大部分知識應用在了耕作這個事情上。產生了生產力的躍進。 第二個是工業革命,正是巴貝奇所處的時代。工業化生產出現,人們使用大型工具,組織起工廠來生產。文藝復興和航海大發現以來,所做的知識儲備在工業革命得到直接物化。 第三個是資訊革命,即現在。我們在使用資訊科技,當然主要就是編寫程式來間接物化知識。讓知識能夠不直接變成機械性的物體也能夠作用於現實。 其實這三次生產力躍進時間附近都有人類的知識大爆發: 農業革命:在國外對應著古希臘文明,在中國對應著三皇五帝到春秋戰國。 工業革命之前是文藝復興和航海大發現。 資訊革命,現在時近現代科學體系的完善。 而有些人把文藝復興稱之為工具理性的復興。文藝復興的本意是指古希臘文明的復興。古希臘文明也是工具理性的一次興盛。即使到了現在,現代科學體系下面的,工具理性依然是核心骨架。在整個人類文明中,工具理性起到了舉足輕重的作用。每一次革命都是知識大爆發的時代,也是我們把知識物化成工具的時代。 到上面為止,我簡單的說了一下自己對於程式設計師思維父類——工具理性的認知。我自己在構建起工程理性這個概念之後。帶著這些認知,看一下軟體(程式設計師思維)的歷史,準確說應該是程式設計的歷史。 關於程式設計的歷史推薦大家看一本書《資訊簡史》,其中第四章到第七章從資訊的角度切入講解了程式的一個起源。包括硬體層面和軟體層面。當然作為Code Monkey我們比較關心的是軟體層面的問題。 上面說到了Ada Lovelace,第一個提出了程式的概念,減小了思維工具對機械工具依賴性的人。而她背後的那個人——實現她的程式硬體的巴貝奇,也是非常重要的。巴貝奇的差分機被認為是第一臺計算機。一個能夠生產數的機器。但是從巴貝奇到第一臺現代計算機ENAIC誕生的一百多年的時間裡面。人們可能更多的是關注,直接用機械來物化思維工具。但是他們在做著為軟體能夠真正意義上的誕生做著基礎性工作——他們創造了資訊理論。 資訊理論的誕生,為我們物化思維工具,嚴格意義上說是間接的物化思維工具提供了一整套的理論依據和方法論。我們不再需要直接去製作機械性的工具就能夠改變現實。我們在資訊理論的基礎上。軟體或者程式設計開始突飛猛進。在短短的60年間,已經完成了幾個量級的跨越。為了更好的展現程式設計的歷史,我們從兩個方面來看。一個是軟體的發展;一個是計算機程式語言的發展歷史。

軟體歷史

計算機軟體技術發展很快。50年前,計算機只能被高素質的專家使用,今天,計算機的使用非常普遍,甚至沒有上學的小孩都可以靈活操作;40年前,檔案不能方便地在兩臺計算機之間進行交換,甚至在同一臺計算機的兩個不同的應用程式之間進行交換也很困難,今天,網路在兩個平臺和應用程式之間提供了無損的檔案傳輸;30年前,多個應用程式不能方便地共享相同的資料,今天,資料庫技術使得多個使用者、多個應用程式可以互相覆蓋地共享資料。瞭解計算機軟體的進化過程,對理解計算機軟體在計算機系統中的作用至關重要。

第一代軟體(1946-1953)

第一代軟體是用機器語言編寫的,機器語言是內建在計算機電路中的指令,由0和1組成。例如計算2+6在某種計算機上的機器語言指令如下: 10110000 00000110 00000100 00000010 10100010 01010000 第一條指令表示將“6”送到暫存器AL中,第二條指令表示將“2”與暫存器AL中的內容相加,結果仍在暫存器AL中,第三條指令表示將AL中的內容送到地址為5的單元中。 不同的計算機使用不同的機器語言,程式設計師必須記住每條及其語言指令的二進位制數字組合,因此,只有少數專業人員能夠為計算機編寫程式,這就大大限制了計算機的推廣和使用。用機器語言進行程式設計不僅枯燥費時,而且容易出錯。想一想如何在一頁全是0和1的紙上找一個打錯的字元! 在這個時代的末期出現了組合語言,它使用助記符(一種輔助記憶方法,採用字母的縮寫來表示指令)表示每條機器語言指令,例如ADD表示加,SUB表示減,MOV表示移動資料。相對於機器語言,用匯編語言編寫程式就容易多了。例如計算2+6的組合語言指令如下: MOV AL,6 ADD AL,2 MOV #5,AL 由於程式最終在計算機上執行時採用的都是機器語言,所以需要用一種稱為彙編器的翻譯程式,把用匯編語言編寫的程式翻譯成機器程式碼。編寫彙編器的程式設計師簡化了他人的程式設計,是最初的系統程式設計師。

第二代軟體(1954-1964)

當硬體變得更強大時,就需要更強大的軟體工具使計算機得到更有效地使用。組合語言向正確的方向前進了一大步,但是程式設計師還是必須記住很多彙編指令。第二代軟體開始使用高階程式設計語言(簡稱高階語言,相應地,機器語言和組合語言稱為低階語言)編寫,高階語言的指令形式類似於自然語言和數學語言(例如計算2+6的高階語言指令就是2+6),不僅容易學習,方便程式設計,也提高了程式的可讀性。 IBM公司從1954年開始研製高階語言,同年發明了第一個用於科學與工程計算的FORTRAN語言。1958年,麻省理工學院的麥卡錫(John Macarthy)發明了第一個用於人工智慧的LISP語言。1959年,賓州大學的霍普(Grace Hopper)發明了第一個用於商業應用程式設計的COBOL語言。1964年達特茅斯學院的凱梅尼(John Kemeny)和卡茨(Thomas Kurtz)發明了BASIC語言。 高階語言的出現產生了在多臺計算機上執行同一個程式的模式,每種高階語言都有配套的翻譯程式(稱為編譯器),編譯器可以把高階語言編寫的語句翻譯成等價的機器指令。系統程式設計師的角色變得更加明顯,系統程式設計師編寫諸如編譯器這樣的輔助工具,使用這些工具編寫應用程式的人,稱為應用程式設計師。隨著包圍硬體的軟體變得越來越複雜,應用程式設計師離計算機硬體越來越遠了。那些僅僅使用高階語言程式設計的人不需要懂得機器語言和組合語言,這就降低了對應用程式設計師在硬體及機器指令方面的要求。因此,這個時期有更多的計算機應用領域的人員參與程式設計。 由於高階語言程式需要轉換為機器語言程式來執行,因此,高階語言對軟硬體資源的消耗就更多,執行效率也較低。由於組合語言和機器語言可以利用計算機的所有硬體特性並直接控制硬體,同時,組合語言和機器語言的執行效率較高,因此,在實時控制、實時檢測等領域的許多應用程式仍然使用匯編語言和機器語言來編寫。 在第一代和第二代軟體時期,計算機軟體實際上就是規模較小的程式,程式的編寫者和使用者往往是同一個(或同一組)人。由於程式規模小,程式編寫起來比較容易,也沒有什麼系統化的方法,對軟體的開發過程更沒有進行任何管理。這種個體化的軟體開發環境使得軟體設計往往只是在人們頭腦中隱含進行的一個模糊過程,除了程式清單之外,沒有其他文件資料。

第三代軟體(1965-1970)

在這個時期,由於用積體電路取代了電晶體,處理器的運算速度得到了大幅度的提高,處理器在等待運算器準備下一個作業時,無所事事。因此需要編寫一種程式,使所有計算機資源處於計算機的控制中,這種程式就是作業系統。 用作輸入/輸出裝置的計算機終端的出現,使使用者能夠直接訪問計算機,而不斷髮展的系統軟體則使計算機運轉得更快。但是,從鍵盤和螢幕輸入輸出資料是個很慢的過程,比在記憶體中執行指令慢得多,這就導致瞭如何利用機器越來越強大的能力和速度的問題。解決方法就是分時,即許多使用者用各自的終端同時與一臺計算機進行通訊。控制這一程式的是分時作業系統,它負責組織和安排各個作業。 1967年,塞繆爾(A.L.Samuel)發明了第一個下棋程式,開始了人工智慧的研究。1968年荷蘭電腦科學家狄傑斯特拉(Edsgar W.Dijkstra)發表了論文《GOTO語句的害處》,指出除錯和修改程式的困難與程式中包含GOTO語句的數量成正比,從此,各種結構化程式設計理念逐漸確立起來。 20世紀60年代以來,計算機用於管理的資料規模更為龐大,應用越來越廣泛,同時,多種應用、多種語言互相覆蓋地共享資料集合的要求越來越強烈。為解決多使用者、多應用共享資料的需求,使資料為儘可能多的應用程式服務,出現了資料庫技術,以及統一管理資料的軟體系統——資料庫管理系統DBMS。 隨著計算機應用的日益普及,軟體數量急劇膨脹,在計算機軟體的開發和維護過程中出現了一系列嚴重問題,例如:在程式執行時發現的問題必須設法改正;使用者有了新的需求必須相應地修改程式;硬體或作業系統更新時,通常需要修改程式以適應新的環境。上述種種軟體維護工作,以令人吃驚的比例消耗資源,更嚴重的是,許多程式的個體化特性使得他們最終成為不可維護的,“軟體危機”就這樣開始出現了。1968年,北大西洋公約組織的電腦科學家在聯邦德國召開國際會議,討論軟體危機問題,在這次會議上正式提出並使用了“軟體工程”這個名詞。

第四代軟體(1971-1989)

20世紀70年代出現了結構化程式設計技術,Pascal語言和Modula-2語言都是採用結構化程式設計規則制定的,Basic這種為第三代計算機設計的語言也被升級為具有結構化的版本,此外,還出現了靈活且功能強大的C語言。 更好用、更強大的作業系統被開發了出來。為IBM PC開發的PC-DOS和為相容機開發的MS-DOS都成了微型計算機的標準作業系統,Macintosh機的作業系統引入了滑鼠的概念和點選式的圖形介面,徹底改變了人機互動的方式。 20世紀80年代,隨著微電子和數字化聲像技術的發展,在計算機應用程式中開始使用影象、聲音等多媒體資訊,出現了多媒體計算機。多媒體技術的發展使計算機的應用進入了一個新階段。 這個時期出現了多用途的應用程式,這些應用程式面向沒有任何計算機經驗的使用者。典型的應用程式是電子製表軟體、文書處理軟體和資料庫管理軟體。Lotus1-2-3是第一個商用電子製表軟體,WordPerfect是第一個商用文書處理軟體,dBase III是第一個實用的資料庫管理軟體。

第五代軟體(1990-今)

第五代軟體中有三個著名事件:在計算機軟體業具有主導地位的Microsoft公司的崛起、物件導向的程式設計方法的出現以及全球資訊網(World Wide Web)的普及。 在這個時期,Microsoft公司的Windows作業系統在PC機市場佔有顯著優勢,儘管WordPerfect仍在繼續改進,但Microsoft公司的Word成了最常用的文書處理軟體。20世紀90年代中期,Microsoft公司將文書處理軟體Word、電子製表軟體Excel、資料庫管理軟體Access和其他應用程式繫結在一個程式包中,稱為辦公自動化軟體。 物件導向的程式設計方法最早是在20世紀70年代開始使用的,當時主要是用在Smalltalk語言中。20世紀90年代,物件導向的程式設計逐步代替了結構化程式設計,成為目前最流行的程式設計技術。物件導向程式設計尤其適用於規模較大、具有高度互動性、反映現實世界中動態內容的應用程式。Java、C++、C#等都是物件導向程式設計語言。 1990年,英國研究員提姆·柏納李(Tim Berners-Lee)建立了一個全球Internet文件中心,並建立了一套技術規則和建立格式化文件的HTML語言,以及能讓使用者訪問全世界站點上資訊的瀏覽器,此時的瀏覽器還很不成熟,只能顯示文字。 軟體體系結構從集中式的主機模式轉變為分散式的客戶機/伺服器模式(C/S)或瀏覽器/伺服器模式(B/S),專家系統和人工智慧軟體從實驗室走出來進入了實際應用,完善的系統軟體、豐富的系統開發工具和商品化的應用程式的大量出現,以及通訊技術和計算機網路的飛速發展,使得計算機進入了一個大發展的階段。 在計算機軟體的發展史上,需要注意“計算機使用者”這個概念的變化。起初,計算機使用者和程式設計師是一體的,程式設計師編寫程式來解決自己或他人的問題,程式的編寫者和使用者是同一個(或同一組)人;在第一代軟體末期,編寫彙編器等輔助工具的程式設計師的出現帶來了系統程式設計師和應用程式設計師的區分,但是,計算機使用者仍然是程式設計師;20世紀70年代早期,應用程式設計師使用複雜的軟體開發工具編寫應用程式,這些應用程式由沒有計算機背景的從業人員使用,計算機使用者不僅是程式設計師,還包括使用這些應用軟體的非專業人員;隨著微型計算機、計算機遊戲、教育軟體以及各種介面友好的軟體包的出現,許多人成為計算機使用者;全球資訊網的出現,使網上衝浪成為一種娛樂方式,更多的人成為計算機的使用者。今天,計算機使用者可以是在學習閱讀的學齡前兒童,可以是在下載音樂的青少年,可以是在準備畢業論文的大學生,可以是在制定預算的家庭主婦,可以是在安度晚年的退休人員,所有使用計算機的人都是計算機使用者。

計算機語言發展歷史

(PS:摘自WIKI)

1940之前

第一個程式語言比現代的計算機還早誕生。首先,這種語言是種編碼(en:code)。 於1801年發明的提花織布機(或稱甲卡提花織布機,英文:en:Jacquard loom),運用打孔卡上的坑洞來代表縫紉織布機的手臂動作,以便自動化產生裝飾的圖案。 Ada Lovelace在1842年至1843年間花費了九個月,將義大利數學家Luigi Menabrea關於查爾斯·巴貝奇新發表機器分析機的回憶錄翻譯完成。她於那篇文章後面附加了一個用分析機計算伯努利數方法的細節,被部分歷史學家認為是世界上第一個電腦程式。這個故事我們上面也說過。 Herman Hollerith在觀察列車長對乘客票根在特定位置打洞的方式後,意識到他可以把資訊編碼記載到打孔卡上,隨後根據這項發現使用打孔卡來編碼並紀錄1890年的人口統計資料。 第一個嚴格意義上的計算機程式碼是針對他們的應用面設計的。在20世紀的前十年主要是用十進位制來算數,後來人們發現不只是用文字,也可以用數字來表現邏輯。舉例來說,阿隆佐·邱奇曾以公式化(formulaic)的方式表達λ演算。圖靈機是一種紙帶標記(tape-marking)機器(就像電話公司用的那種)操作方法抽象化後的集合。圖靈機這種透過有限數字(finite number)呈現機器的方式,奠定了程式如同馮·諾伊曼結構計算機中的資料一樣地儲存的基礎。但不同於λ演算,圖靈機的程式碼並沒有辦法成為高階程式語言的基石,這是是因為它主要的用途是分析演算法的複雜度。 就像許多歷史上的”第一次”一樣,第一個現代程式語言也很難界定。最一開始是因為硬體限制而限定了語言,打孔卡允許80行(column)的長度,但某幾行必須用來記錄卡片的順序。FORTRAN則納入了一些與英文字詞相同的關鍵字,像是”IF”、”GOTO”(原字詞為go to),以及”CONTINUE”。之後採用磁鼓(magnetic drum)作為記憶體使用,也代表計算機程式也必須插入(interleave)到磁鼓的轉動(rotation)中。和現今比較起來,這也讓程式語言必須更加依賴硬體(hardware-dependent)。 對部分的人認為必須在”程式語言”的狀態確立之前,根據能力(power)以及可讀性(human-readability)的程度來決定歷史上第一個程式語言是什麼語言。提花織布機和查爾斯·巴貝奇所製作的差分機(en:Difference Engine)都具備在大量限制下,簡單描述機器應執行行為的語言。也有種並非設計給人類運用的受限特定領域語言(en:domain-specific language),是將打孔卡運用到自動演奏鋼琴(en:player piano)上。

1940年代

最早被確認的現代化、電力啟動(electrically powered)的計算機約在1940年代被創造出來。程式設計師在有限的速度及記憶體容量限制之下,撰寫人工調整(hand tuned)過的組合語言程式。而且很快就發現到使用組合語言的這種撰寫方式需要花費大量的腦力(intellectual effort)而且很容易出錯(error-prone)。 Konrad Zuse於1948年發表了他所設計的Plankalkül程式語言的論文[1]。但是在他有生之年卻未能將該語言實作,而他原本的貢獻也被其他的發展所孤立。 在這段期間被開發出來的重要語言包括有:

  • 1943 – Plankalkül (Konrad Zuse)
  • 1943 – ENIAC coding system
  • 1949 – C-10

1950與1960年代

有三個現代程式語言於1950年代被設計出來,這三者所衍生的語言直到今日仍舊廣泛地被採用:

  • Fortran (1955),名稱取自”FORmula TRANslator”(公式翻譯器),由約翰·巴科斯等人所發明;
  • LISP,名稱取自”LISt Processor”(列舉處理器),由約翰·麥卡錫等人所發明;
  • COBOL,名稱取自”COmmon Business Oriented Language”(通用商業導向語言),由被葛麗絲·霍普深刻影響的Short Range Committee所發明。

另一個1950年代晚期的里程碑是由美國與歐洲計算機學者針對”演算法的新語言”所組成的委員會出版的ALGOL 60報告(名稱取自”ALGOrithmic Language”(演算法語言))。這份報告強化了當時許多關於計算的想法,並提出了兩個語言上的創新功能:

  • 巢狀區塊結構:可以將有意義的程式碼片段群組成一個區塊(block),而非轉成分散且特定命名的程式。也就是我們所熟悉的模組化設計。
  • 詞彙範圍(lexical scoping):區塊可以有區塊外部無法透過名稱存取,屬於區塊本身的變數、程式以及函式。就是我們所熟悉的作用域。

另一個創新則是關於語言的描述方式:一種名為巴科斯-諾爾正規化 (BNF)的數學化精確符號被用於描述語言的語法。之後的程式語言幾乎全部都採用類似BNF的方式來描述程式語法中上下文無關的部份。BNF主要使用在了Algol 60的設計上面。而Algol 60對之後語言的設計上帶來了特殊的影響,在其他部分的語言設計中這種設計思想很快的就被廣泛採用。並且後續為了開發Algol的擴充子集合,設計了一個名為Burroughs(en:Burroughs large systems)的大型系統。而延續Algol的關鍵構想所產生的成果就是ALGOL 68:

  • 語法跟語意變的更加正交(orthogonal)
  • 採用匿名的歷程(routines)
  • 採用高階(higher-order)功能的遞迴式輸入(typing)系統等等。

整個語言及語意的部分都透過為了描述語言而特別設計的Van Wijngaarden grammar來進行正式的定義,而不僅止於上下文無關的部份。Algol 68一些較少被使用到的語言功能(如同步與並列區塊)、語法捷徑的複雜系統,以及型態自動強制轉換(coercions),使得實作者興趣缺缺,也讓Algol 68獲得了很難用(diffcult)的名聲。尼克勞斯·維爾特就乾脆離開該設計委員會,另外在開發出更簡單的Pascal語言。 在這段期間被開發出來的重要語言包括有:

  • 1951 – Regional Assembly Language
  • 1952 – Autocode
  • 1954 – FORTRAN
  • 1954 – IPL (LISP的先驅)
  • 1955 – FLOW-MATIC (COBOL的先驅)
  • 1957 – COMTRAN (COBOL的先驅)
  • 1958 – LISP
  • 1958 – ALGOL 58
  • 1959 – FACT (COBOL的先驅)
  • 1959 – COBOL
  • 1962 – APL
  • 1962 – Simula
  • 1962 – SNOBOL
  • 1963 – CPL (C的先驅)
  • 1964 – BASIC
  • 1964 – PL/I
  • 1967 – BCPL (C的先驅)

1967-1978:確立了基礎正規化

1960年代晚期至1970年代晚期的期間中,程式語言的發展也有了重大的成果。大多數現在所使用的主要語言正規化都是在這段期間中發明的:

  • Simula,於1960年代晚期由奈加特與Dahl以Algol 60超集合的方式發展,同時也是第一個設計支援物件導向進行開發的程式語言。
  • C,於1969至1973年間由貝爾實驗室的研究人員丹尼斯·裡奇與肯·湯普遜所開發,是一種早期的系統程式設計(en:system programming)語言。
  • Smalltalk,於1970年代中期所開發,是一個完全從零開始(ground-up)設計的物件導向程式語言。
  • Prolog,於1972年由Colmerauer、Roussel,以及Kowalski所設計,是第一個邏輯程式語言。
  • ML,於1973年由羅賓·米爾納所發明,是一個基於Lisp所建構的多型(polymorphic)型態系統,同時也是靜態型別函式程式語言的先驅。

這些語言都各自演展出自己的家族分支,現今多數現代程式語言的祖先都可以追朔他們其中至少一個以上。 在1960年代以及1970年代中結構化程式設計的優點也帶來許多的爭議,特別是在程式開發的過程中完全不使用GOTO。這項爭議跟語言本身的設計非常有關係:某些語言並沒有包含GOTO,這也強迫程式設計師必須結構化地編寫程式。儘管這個爭議在當時吵翻了天,但幾乎所有的程式設計師都同意就算語言本身有提供GOTO的功能,在除了少數罕見的情況下去使用GOTO是種不良的程式風格。結果是之後世代的程式語言設計者發覺到結構化程式語言的爭議實在既乏味又令人眼花撩亂。 在這段期間被開發出來的重要語言包括有:

  • 1968 – Logo
  • 1970 – Pascal
  • 1970 – Forth
  • 1972 – C語言
  • 1972 – Smalltalk
  • 1972 – Prolog
  • 1973 – ML
  • 1975 – Scheme
  • 1978 – SQL (起先只是一種查詢語言,擴充之後也具備了程式結構)

1980年代:增強、模組、效能

1980年代的程式語言與之前相較顯得更為強大。C++合併了物件導向以及系統程式設計。美國政府標準化一種名為Ada的系統程式語言並提供給國防承包商使用。日本以及其他地方運用了大量的資金對採用邏輯程式語言結構的第五代語言進行研究。函式程式語言社群則把焦點轉移到標準化ML及Lisp身上。這些活動都不是在開發新的正規化,而是在將上個世代發明的構想進一步發揚光大。 然而,在語言設計上有個重大的新趨勢,就是研究運用模組或大型組織化的程式單元來進行大型系統的開發。Modula、Ada,以及ML都在1980年代發展出值得注意的模組化系統。模組化系統常拘泥於採用泛型程式設計結構:

  • 泛型存在(generics being)
  • 本質(essence)
  • 引數化模組(parameterized modules)

儘管沒有出現新的主要程式語言正規化,許多研究人員仍就擴充之前語言的構想並將它們運用到新的內容上。舉例來說,Argus以及Emerald系統的語言配合面嚮物件語言運用到分散式系統上。 1980年代的程式語言實際情況也有所進展。計算機系統結構中RISC假定硬體應當為編譯器設計,而並非為人類設計。藉由中央處理器速度增快的幫助,編譯技術也越來越進展神速,RISC的進展對高階語言編譯技術發展來不小的貢獻。 在這段期間被開發出來的重要語言包括有:

  • 1980 – Ada
  • 1983 – C++ (就像有類別的C)
  • 1984 – Common Lisp
  • 1985 – Eiffel
  • 1986 – Erlang
  • 1987 – Perl
  • 1988 – Tcl
  • 1989 – FL (Backus)

1990年代:網際網路時代

1990年代未見到有什麼重大的創新,大多都是以前構想的重組或變化。這段期間主要在推動的哲學思想是提升程式設計師的生產力。許多”快速應用程式開發” (RAD) 語言也應運而生,這些語言大多都有相應的整合開發環境、垃圾回收等機制,且大多是先前語言的衍生語言。這型別的語言也大多是物件導向的程式語言,包含有Object Pascal、Visual Basic,以及C#。Java則是更加保守的語言,也具備垃圾回收機制。與其他類似語言相比,也受到更多的觀注。新的指令碼語言則比RAD語言更新更好。這種語言並非直接從其他語言衍生,而且新的語法更加開放地(liberal)與功能契合。雖然指令碼語言比RAD語言來的更有生產力,但大多會有因為小程式較為簡單,但是大型程式則難以使用指令碼語言撰寫並維護的顧慮[來源請求]。儘管如此,指令碼語言還是網路層面的應用上大放異彩。 在這段期間被開發出來的重要語言包括有:

  • 1990 – Haskell
  • 1991 – Python
  • 1991 – Visual Basic
  • 1993 – Ruby
  • 1993 – Lua
  • 1994 – CLOS (part of ANSI Common Lisp)
  • 1995 – Java
  • 1995 – Delphi (Object Pascal)
  • 1995 – JavaScript
  • 1995 – PHP
  • 1997 – REBOL
  • 1999 – D

現今的趨勢

程式語言持續在學術及企業兩個層面中發展進化,目前的一些趨勢包含有:

  • 在語言中增加安全性與可靠性驗證機制:額外的堆疊檢查、資訊流(information flow)控制,以及靜態執行緒安全。
  • 提供模組化的替代機制:混入(en:mixin)、委派(en:delegates),以及觀點導向。
  • 元件導向(component-oriented)軟體開發
  • 超程式設計、反射或是存取抽象語法樹(en:Abstract syntax tree)
  • 更重視分散式及移動式的應用。
  • 與資料庫的整合,包含XML及關聯式資料庫。
  • 支援使用Unicode編寫程式,所以原始碼不會受到ASCII字符集的限制,而可以使用像是非拉丁語系的指令碼或延伸標點符號。
  • 圖形化使用者介面所使用的XML(XUL、XAML)。

在這段期間被開發出來的重要語言包括有:

  • 2001 – C#
  • 2001 – Visual Basic .NET
  • 2002 – F#
  • 2003 – Scala
  • 2003 – Factor
  • 2006 – Windows PowerShell
  • 2007 – Clojure
  • 2009 – Go

需求

在回顧這些歷史的時候發現,我們無論是創造程式語言還是計算機,或者軟體也好,最終的目的都是為了兩個字——需求。我們遵循著工具理性的框架,追尋著完成需求的目標。而在計算機發展的過程的過程中,主要的需求有哪些?

  1. 創造工具來滿足人們現實生活中的需求,比如金融工具、QQ、微信
  2. 不斷創造更加好用的硬體基礎。並且創造響應的軟體來適應更快的硬體。
  3. 隨著硬體規模和軟體規模的不斷擴大,發展相應的理論去控制規模擴大帶來的影響,即控制複雜性。

當然,我們必須先解決的第一個需求就是我們創造計算機的原始需求:創造工具來滿足人們現實生活中的需求。但是像絕大部分工具一樣,一旦我們穿鑿了它,它本省也會衍生出來很多需求。工具本身也需要演化。而工具本身的需求就是後兩條。 滿足第一條需求的方式,千奇百怪!基本上會涉及到人類現已掌握的知識的各個層面。比如一個圖書分享網站,最起碼要涉及:管理學、心理學、營銷、產品設計、美術設計、互動設計、圖書館管理等等。而這些知識通過軟體設計物化在了網站這個東西上面。然後我們就能通過網站這個工具,來滿足我們圖書分享的需求。 但是,我們能夠發現,其實程式設計(程式設計師思維的最主要的展現形式)雖然與第一條需求有關,但又關係不是很大。的確,我們是通過軟體設計這件事情,物化了我們的知識。但是在這個過程中,軟體設計並不關係人們的需求具體是什麼,是圖書網站,還是聊天軟體和軟體設計並沒有直接的關係。相對來說,軟體設計比較關心的是後面兩個需求:來自工具本身的需求。用一句話說就是:Make it work, Keep it simple。 首先你必須讓這個工具能夠工作,其次你必須讓這個工具能夠持續穩定的工具的工作(不會因為規模擴大,複雜性增長而招致災難)。而這就是程式設計師思維中在工具理性下面最為核心的兩個具體的概念:Make it work, keep it simple。 明白了這一點,我們再回過頭去看一下剛剛所述的軟體和程式設計的歷史。剛開始人們需要有一個機器替代人進行計算於是有了差分機和ENIAC,有了硬體之後,自然就需要一種驅動機制能夠讓這些機器能夠運轉起來,於是我們發明了程式語言和軟體。而隨著硬體的不斷髮展和軟體規模的不斷擴大,人們發現最原始的計算機語言(機器語言)不使用於快速開發。於是就有了彙編這樣的低階語言。後來低階語言也被證明在開發速度上存在缺陷,也不太適合快速開發,於是我們有了高階語言,比如Lisp、C、fortran。剛開始的時候這些高階語言,的確能夠滿足快速開發的需求。但是,隨著軟體規模的不斷擴大。我們開始發現:靠,我們創造了軟體卻控制不了軟體。計算機和軟體這個工具的規模已經超出了人類能夠認知的規模,它的複雜性已經開始變得不可控了。這怎麼能行呢,於是我們開始創造了物件導向和軟體工程等理論工具來幫助我們控制這種軟體複雜性。 而到了今天,我們看一下我們日常Coding中常見的那些理論的方法,基本上也都是圍繞著Make it work, keep simple展開的。其實這兩個問題是不能夠割裂開闡述的,為了理解上方面,我們就先單獨說吧,不過中間會有概念的穿插。

Make it work(程式設計正規化,程式語言的世界觀)

如何讓計算機按照我們制定的方式工作,如何讓軟體能夠按照我們假象的方式執行?我們把完成這兩個需求的過程叫做程式設計。就是我們在前文中所說:程式設計就是利用計算機將知識物化,並利用知識的可復現性來對現實產生作用。但是,這只是一個概念啊。如何才能把它落實到實踐上呢?這就要說一下程式設計正規化了,我們對於程式語言是什麼的世界觀。 程式設計範型或程式設計正規化(英語:Programming paradigm),(範即模範之意,正規化即模式、方法),是一類典型的程式設計風格,是指從事軟體工程的一類典型的風格(可以對照方法學)。如:函數語言程式設計、程式程式設計、物件導向程式設計、指令式程式設計等等為不同的程式設計範型。 程式設計範型提供了(同時決定了)程式設計師對程式執行的看法。例如,在物件導向程式設計中,程式設計師認為程式是一系列相互作用的物件,而在函數語言程式設計中一個程式會被看作是一個無狀態的函式計算的序列。程式設計正規化就是我們程式設計的世界觀,他決定了程式在我們眼中是個什麼樣,而我們又怎樣去操作或者使用程式語言。 正如軟體工程中不同的群體會提倡不同的“方法學”一樣,不同的程式語言也會提倡不同的“程式設計範型”。一些語言是專門為某個特定的範型設計的(如Smalltalk和Java支援物件導向程式設計,而Haskell和Scheme則支援函數語言程式設計),同時還有另一些語言支援多種範型(如Ruby、Common Lisp、Python和Oz)。 很多程式設計範型已經被熟知他們會禁止使用哪些技術,同時又允許使用哪些技術。 例如,純粹的函數語言程式設計不允許有副作用;結構化程式設計不允許使用goto。可能是因為這個原因,新的範型常常被那些慣於較早的風格的人認為是教條主義或過分嚴格。然而,這樣避免某些技術反而更加證明了關於程式正確性——或僅僅是理解它的行為——的法則,而不用限制程式語言的一般性。 程式設計範型和程式語言之間的關係可能十分複雜,由於一個程式語言可以支援多種範型。例如,C++設計時,支援過程化程式設計、物件導向程式設計以及泛型程式設計。然而,設計師和程式設計師們要考慮如何使用這些範型元素來構建一個程式。一個人可以用C++寫出一個完全過程化的程式,另一個人也可以用C++寫出一個純粹的物件導向程式,甚至還有人可以寫出雜揉了兩種範型的程式。 下面是我們比較常見的幾種“程式語言的世界觀”:

名稱 代表語言 核心概念 執行機制 關鍵突破 實現原理 主要目的 常見應用
命令式/過程式(Impreative/procedural) Fortran/Pascal/C 命令、過程 命令執行 突破單一主程式和非結構化程式的限制 引入邏輯控制和子程式 模擬機器思維,實現自頂向下的模組設計 互動式、事件驅動型系統、數值計算等
函式式、應用式(Functional、Applicative) Scheme/Haskell 函式 表示式計算 突破機器思維的限制引入高階函式、將函式作為資料處理 模擬數學思維,簡化程式碼,減少副作用 微積分計算、數學邏輯、博弈等
邏輯式 Prolog/Mercury 斷言 邏輯推理 突破邏輯與控制粘合的限制 利用推理引擎在已知的事實和規則的基礎上進行邏輯推斷 專注邏輯分析、減少程式碼 機器證明、專家系統、自然語言處理、語義網、決策分析、業務規則管理等
物件式(Object-Oriented) Smaltalk/Java/Objc 物件 物件間資訊互動 突破資料與程式碼分隔的限制 引入封裝、繼承和多型機制 迎合人類認知模式,提高軟體的易用性和重用性 大型複雜互動系統等
併發式/並行式(Concurrent/Parallel) Erlang/Oz 程式、執行緒 程式、執行緒、協程間通訊與同步 突破序列的限制 引入並行的執行緒模組以及模組間的通訊與同步機制 充分利用資源、提供執行效率、提高軟體的響應能力 圖形介面,IO處理,多工系統,計算密集型系統
泛型式(Generic) Ada/Eiffel/C++ 演算法 演算法例項化(多發生於編譯期) 突破靜態型別語言的限制 利用模板推遲型別指定 提高演算法的普適性 普適性演算法如排序、搜尋等,集合類容器等
超程式設計(Meta programming) Lisp/Ruby/JavaScript 元程式 動態生成程式碼或者自動修改執行指令 突破語言的常規語法限制 利用程式碼生成或語言內奸的反射、動態等機制,將程式語言作為資料處理 減少手工編碼、提高語言級別 自動程式碼生成、定義結構化配置檔案,IDE、編譯器,直譯器,人工智慧、領域特定語言等
切面式(Aspect-Oriented) AspectJ/AspectC++ 切面 在接入點處執行建議 突破橫切關注點無法模組化的限制 通過編制將附加行為嵌入主題程式 實現橫切關注點分割 日誌輸出、程式碼耿宗、效能監控、異常處理、安全檢查等
時間驅動(Event-Driven) C#/VB.NET 事件 監聽器收到事件後做出響應 突破順序、同步的流程限制 引入控制反轉和非同步機制 呼叫者與被呼叫者在程式碼和時間上雙重解耦 圖形介面、網路應用、伺服器、非同步輸入等、DOM等

為了能夠深入的理解每一種程式設計正規化對我們程式設計實踐的影響,我們來看一下用上面提到的不同的程式設計正規化的代表語言去寫一個我們常見的快速排序演算法:

網址: http://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F

同樣是一個快速排序演算法,每一種程式設計正規化下他們實現的思路真的是千差萬別。最主要的是各有千秋。我們很難說某一種程式設計正規化比另外一種程式設計正規化優秀,也很難說某一種程式設計正規化比另外一種要壞。我們只能說某一種程式設計正規化在某些特殊的情境下或者需求下要比其他的合適。因為他們就是為這些需求而生的。而其他的程式設計正規化是為另外的程式設計正規化。如果剪子砸不了核桃,不是剪刀的問題,而是你沒有使用對工具的問題。

而在我們看了這麼多的程式設計正規化,接受了這麼多的對於程式的世界觀之後。就會問,這些程式設計正規化之間有沒有一些內在的聯絡呢?這個問題一個明顯的答案是:他們都是為了解決問題而生的。但是這個答案似乎有點欠妥。因為,我們是說的內在聯絡。而不是問他們的目標。 Pascal之父尼克勞斯·沃思曾經給出過程式的一個定義:演算法+資料結構=程式,後來他又對演算法進行了定義:邏輯+控制=演算法,最後一個完整的表述是:程式=(邏輯+控制)+資料結構。雖然他提出這個概念的時候是針對命令式或者過程式的程式的。但是我們在很多程式設計正規化中依稀能夠看到:邏輯+控制+資料結構=程式的影子。 我們能夠看到無論是在哪一種程式設計正規化指導下的語言。我們都能夠看到相同的“控制”結構——順序結構、分支結構、迴圈結構。我們也都能夠看到是與非的邏輯判斷。資料結構更不用說。沒有任何語言不去運算元據的吧。所以命令式是一種基礎正規化。同樣為基礎正規化的還有函式式和邏輯式。而比較有意思的是,這幾種基礎程式設計正規化各有所重。指令式程式設計著重控制和資料結構,函數語言程式設計著重邏輯、控制和資料結構,邏輯式著重邏輯和資料結構。這幾種程式設計正規化很好的構建起了,程式的基礎組成部分的概念。命令式可以告訴你控制這個東西是怎麼回事,怎麼去用。邏輯式,像數學思維一樣的精巧告訴你邏輯的魅力。函式式,告訴你怎樣組合起來使用邏輯和控制。而他們又共同在講著資料結構的概念。其實,邏輯+控制+資料結構的世界觀來自於我們對於計算機這個機械器件的認知。 其他的程式設計正規化我們稱之為高階正規化,因為他們在基礎正規化概念基礎之上增加了自己對於程式的額外的一些認知。這裡著重說一下,物件導向程式設計正規化。物件導向認為程式就是資訊互動。他把資料結構放到了一個非常重要的位置。突破了資料域程式碼隔離的限制。讓資料能夠與邏輯和控制緊密的結合在一起。這種思維方式,非常符合人類的認知模式。物件導向,通過人類思維中常用的手段——抽象,提供了一種新的組織程式的方式。而這種方式,極大的提高了軟體的易用性和重用性,同時非常好的控制了整個軟體的複雜性。 如果想要了解更多關於程式設計正規化的東西,推薦看一下《冒號學堂》。

Keep it simple(複雜性控制)

首先我們嘗試先構建起復雜性的概念。什麼是複雜性? 複雜,字面意思理解,就是一個東西極其龐大承載的東西太多而我們理解或者認知起來已經很困難的時候,我們就說這個東西很複雜。而《現代漢語詞典》對於複雜的解釋也符合我們的直覺:

複雜性,就是在把複雜作為一種特性了而已。就是說一件事物具有了一種“多而雜”的特性。而現代的複雜性科學認為複雜性可分為:情景複雜性(事物固有屬性)和認知複雜性,而且兩者互為因果。 那麼軟體為什麼會有複雜性呢?或者說程式設計為什麼會帶來複雜性呢?這和程式設計本身有關係。我們回顧一下前面我們對於程式設計的定義:程式設計就是利用計算機將知識物化,並利用知識的可復現性來對現實產生作用。 那麼說,程式設計就是在為整個軟體系統增加知識。在增加整個系統的資訊總量。通過資訊理論我們知道,當一個系統中的資訊越多的時候,其資訊熵值就越大。熵值越大,我們理解難度就越大,勢必會帶來複雜性增加。於是,複雜性一個比較淺顯的數學定義就是資訊熵。

上述公式是夏農資訊熵的公式,現在也可以理解成軟體複雜性的公式。我們容易發現,軟體設計這個事情,本事就是在增加複雜性的。他在不斷的往整個軟體系統中,增加資訊,資訊越多,我們越難去理解這個軟體。這個軟體就越複雜,當這種複雜到了一定的量級的時候,就爆發了軟體危機。於是我們需要擴充一下我們對於程式設計的定義:程式設計在完成思維工具物化的同時,還需要儘可能的控制資訊熵的增長,來降低系統的複雜性。 但是,人們總是聰明的,有了問題,我們就去發明新的工具啊。於是我們就有了一系列複雜性控制的理論。這裡我們不去探討更普遍層面的複雜性,我們先只看一下,程式設計,這一個我們比較關心的實物的複雜性控制的問題(要想對複雜性有一個淺顯的認識可以看一下《探索複雜性》和《資訊簡史》)。 在程式設計層面上,我們沒有很好理論化的東西去控制複雜性。但是我們創造了一系列的方法來控制複雜性。我們把這一些列的方法叫做“最佳實踐“。如果你去看一些軟體工程或者物件導向設計的書的時候,這個詞可能經常遇到。而我們的最佳實踐有哪些呢? 結構化程式設計、物件導向程式設計、軟體工程、敏捷開發、UML、領域建模…….不一而足。 關於“最佳實現”的具體細節的東西,有很多寫的非常好的書中都有描述我們這裡就不再贅述了。我們著重關心一點,如何去評價一個設計或者軟體的複雜性呢?因為你不知道,如何評價或者衡量自己設計的複雜性的話,勢必也不知道如何改進。 而評價的方式比較靠譜的有兩個:

  1. 高內聚低耦合
  2. 是否滿足設計模式的六大基本原則

高內聚低耦合

這個是我們剛開始提出的問題之一。內聚和耦合是一對相對的概念。相輔相成。兩者雖然有聯絡,但不是必然聯絡。高內聚的設計不一定低耦合,低耦合的東西也不一定高內聚。那麼內聚性和耦合性是什麼,如何去評價呢?

內聚性

在程式設計中,內聚性是指機能相關的程式組合成一模組的程度。應用在物件導向程式設計中,若服務特定型別的方法在許多方面都很類似,則此型別即有高內聚性。在一個高內聚性的系統中,程式碼可讀性及複用的可能性都會提高,程式雖然複雜,但可被管理。 以下的情形會降低程式的內聚性:

  • 許多機能封裝在一型別內,可以藉由方法供外界使用,但機能彼此類似之處不多。
  • 在方法中進行許多不同的機能,使用的是相關性低或不相關的資料。

低內聚性的缺點如下:

  • 增加理解模組的困難度。
  • 增加維護系統的困難度,因為一個邏輯修改會影響許多模組,而一個模組的修改會使得一些相關模組也要修改。
  • 增加模組複用困難度,因為大部份的應用程式無法複用一個由許多不一定相關的機能組成的模組。
內聚性的衡量

內聚性是一種非量化的量測,可利用評量規準來確認待確認原始碼的內聚性的分類。內聚性的分類如下,由低到高排列:

  1. 偶然內聚性(Coincidental cohesion,最低)偶然內聚性是指模組中的機能只是剛好放在一起,模組中各機能之間唯一的關係是其位在同一個模組中(例如:“工具”模組)。
  2. 邏輯內聚性(Logical cohesion)邏輯內聚性是隻要機能只要在邏輯上分為同一類,不論各機能的本質是否有很大差異,就將這些機能放在同一模組中(例如將所有的滑鼠和鍵盤都放在輸入處理副程式中)。
  3. 時間性內聚性(Temporal cohesion)時間性內聚性是指將相近時間點執行的程式,放在同一個模組中(例如在捕捉到一個異常後呼叫一函式,在函式中關閉已開啟的檔案、產生錯誤日誌、並告知使用者)。
  4. 程式內聚性(Procedural cohesion)程式內聚性是指依一組會依照固定順序執行的程式放在同一個模組中(例如一個函式檢查檔案的許可權,之後開啟檔案)。
  5. 聯絡內聚性(Communicational cohesion)聯絡內聚性是指模組中的機能因為處理相同的資料,因此放在同一個模組中(例如一個模組中的許多機能都訪問同一個記錄)。
  6. 依序內聚性(Sequential cohesion)依序內聚性是指模組中的各機能彼此的輸入及輸出資料相關,一模組的輸出資料是另一個模組的輸入,類似工廠的生產線(例如一個模組先讀取檔案中的資料,之後再處理資料)。
  7. 功能內聚性(Functional cohesion,最高)功能內聚性是指模組中的各機能是因為它們都對模組中單一明確定義的任務有貢獻(例如XML字串的詞法分析)。

由賴瑞·康斯坦丁、愛德華·尤登及史蒂夫·麥康奈爾等人的研究都提出偶然內聚性和邏輯內聚性是不好的,聯絡內聚性和依序內聚性是好的,而功能內聚性是最理想的狀態。 當我們拿到一個設計的時候,完全可以通過上述的幾個層面去衡量這個設計的內聚性。

耦合性

內聚性是一個和耦合性相對的概念,一般而言低耦合性代表高內聚性,反之亦然。耦合性和內聚性都是由提出結構化設計概念的賴瑞·康斯坦丁所提出[1]。低耦合性是結構良好程式的特性,低耦合性程式的可讀性及可維護性會比較好。 耦合性可以是低耦合性(或稱為鬆散耦合),也可以是高耦合性(或稱為緊密耦合)。以下列出一些耦合性的分類,從高到低依序排列:

  1. 內容耦合(content coupling,耦合度最高)也稱為病態耦合(pathological coupling)是指一個模組依賴另一個模組的內部作業(例如,訪問另一個模組的局域變數),因此修改第二個模組處理的資料(位置、形態、時序)也就影響了第一個模組。
  2. 共用耦合(common coupling)也稱為全域性耦合(global coupling.) 是指二個模組分享同一個全域性變數,因此修改這個共享的資源也就要更動所有用到此資源的模組。
  3. 外部耦合(external coupling)發生在二個模組共用一個外加的資料格式、通訊協議或是裝置介面,基本上和模組和外部工具及裝置的溝通有關。
  4. 控制耦合(control coupling)是指一個模組藉由傳遞“要做什麼”的資訊,控制另一個模組的流程(例如傳遞相關的旗標)。
  5. 特徵耦合(stamp coupling)也稱為資料結構耦合,是指幾個模組共享一個複雜的資料結構,而每個模組只用其中的一部份,甚至各模組用到的部份不同(例如傳遞一筆記錄給一個函式,而函式只需要其中的一個欄位。
  6. 資料耦合(data coupling)是指模組藉由傳入值共享資料,每一個資料都是最基本的資料,而且只分享這些資料(例如傳遞一個整數給計算平方根的函式)。
  7. 資訊耦合(message coupling,是無耦合之外,耦合度最低的耦合)可以藉由以下二個方式達成:狀態的去中心化(例如在物件中),元件間利用傳入值或資訊傳遞 (電腦科學)來通訊。
  8. 無耦合模組完全不和其他模組交換資訊。

耦合性和內聚性二個名詞常一起出現,用來表示一個理想模組需要有的特點,也就是低耦合性及高內聚性。耦合性著重於不同模組之間的相依性,而內聚性著重於一模組中不同功能之間的關係性。低內聚性表示一個模組中的各機能之間沒什麼關係,當模組擴充時常常會出現問題。 相對於內聚性而言耦合性是一個相對比較好度量的概念。已經有比較好的數學公式來比較精準的衡量耦合性。 以下是一種計算模組耦合性的方法[: 對於資料和控制流的耦合:

  • di: 輸入資料引數的個數
  • ci: 輸入控制引數的個數
  • do: 輸出資料引數的個數
  • co: 輸出控制引數的個數

全域性耦合:

  • gd: 用來儲存資料的全域性變數
  • gc: 用來控制的全域性變數

環境耦合:

  • w: 此模組呼叫的模組個數(扇出)
  • r: 呼叫此模組的模組個數(扇入)

若Coupling(C)數值越大,表示模組耦合的情形越嚴重,數值一般會界於0.67(低度耦合)到1.0(高度耦合)之間。 到此,我們已經構建起了高內聚和低耦合的概念。並且知道了如何去使用這兩個概念去控制複雜性。

設計模式的六大原則

設計模式這個東西就不細說了,大家都知道。但是如何去評價一個設計模式呢?或者是什麼知道我們設計“設計模式”的,那就是六大原則:

  1. 單一職責原則
  2. 里氏替換原則
  3. 依賴倒置原則
  4. 介面隔離原則
  5. 迪米特法則
  6. 開閉原則

這裡偷點懶不詳細贅述了,可以參考六大原則

總結

在上面的敘述中,我們講了工具理性,之後從工具理性衍生出了程式設計師思維的定義。通過回顧歷史我們定義了程式設計,並且指出了程式設計中的兩個主要的問題:Make it work, keep it simple。之後我們針對這兩個問題,分別闡述了不同的方法論。以程式設計正規化為主的make it work,和以複雜性控制為主的Keep it simple。同時提到了,在這個方法論之下的一些最佳實踐。至此我們構建起了一個程式設計師的思維框架。

PS:個人見解,僅供參考。

最後用一幅圖來總結上面我們所述:structs_thouths

相關文章