程序員技能層次模型
編程技能層次
編程技能層次,指的程序員設(shè)計(jì)和編寫程序的能力。這是程序員的根本。
0段—非程序員:
初學(xué)編程者,遇到問題,完全是懵懵懂懂,不知道該怎么編程解決問題。也就是說,還是門外漢,還不能稱之為“程序員”。計(jì)算機(jī)在他面前還是一個(gè)神秘的黑匣子。
1段—基礎(chǔ)程序員:
學(xué)習(xí)過一段時(shí)間編程后,接到任務(wù),可以編寫程序完成任務(wù)。
編寫出來的代碼,正常情況下是能夠工作的,但在實(shí)際運(yùn)行中,碰到一些特殊條件就會(huì)出現(xiàn)各類BUG。也就是說,具備了開發(fā)Demo軟件的能力,但開發(fā)的軟件真正交付給客戶使用,恐怕會(huì)被客戶罵死。
程序員程序是寫好了,但到底為什么它有時(shí)能正常工作,有時(shí)又不行,程序員自己也不知道。
運(yùn)行中遇到了bug,或者需求改變,需要修改代碼或者添加代碼,很快程序就變得結(jié)構(gòu)混亂,代碼膨脹,bug叢生。很快,就連最初的開發(fā)者自己也不愿意接手維護(hù)這個(gè)程序了。
2段—數(shù)據(jù)結(jié)構(gòu):
經(jīng)過一段時(shí)間的編程實(shí)踐后,程序員會(huì)認(rèn)識(shí)到“數(shù)據(jù)結(jié)構(gòu)+算法=程序”這一古訓(xùn)的含義。他們會(huì)使用算法來解決問題。進(jìn)而,他們會(huì)認(rèn)識(shí)到,算法本質(zhì)上是依附于數(shù)據(jù)結(jié)構(gòu)的,好的數(shù)據(jù)結(jié)構(gòu)一旦設(shè)計(jì)出來,那么好的算法也會(huì)應(yīng)運(yùn)而生。
設(shè)計(jì)錯(cuò)誤的數(shù)據(jù)結(jié)構(gòu),不可能生長(zhǎng)出好的算法。
記得某一位外國(guó)先賢曾經(jīng)說過:“給我看你的數(shù)據(jù)結(jié)構(gòu)!”
3段—面向?qū)ο螅?/strong>
再之后,程序員就會(huì)領(lǐng)略面向?qū)ο蟪绦蛟O(shè)計(jì)的強(qiáng)大威力。大多數(shù)現(xiàn)代編程語言都是支持面向?qū)ο蟮?。但并不是說,你使用面向?qū)ο缶幊陶Z言編程,你用上了類,甚至繼承了類,你就是在寫面向?qū)ο蟮拇a了。
我曾經(jīng)見過很多用Java,Python,Ruby寫的面向過程的代碼。
只有你掌握了接口,掌握了多態(tài),掌握了類和類,對(duì)象和對(duì)象之間的關(guān)系,你才真正掌握了面向?qū)ο缶幊碳夹g(shù)。
就算你用的是傳統(tǒng)的不支持面向?qū)ο蟮木幊陶Z言,只要你心中有“對(duì)象”,你依然可以開發(fā)出面向?qū)ο蟮某绦颉?/p>
如,我用C語言編程的時(shí)候,會(huì)有意識(shí)的使用面向?qū)ο蟮募记蓙砭帉懞驮O(shè)計(jì)程序。用struct來模擬類,把同一類概念的函數(shù)放在一起模擬類。如果你懷疑用C語言是否能編寫出面向?qū)ο蟮拇a,你可以看一下Linux內(nèi)核,它是用C語言編寫的,但你也可以看到它的源代碼字里行間散發(fā)出的濃濃的“對(duì)象”的味道。
真正掌握面向?qū)ο缶幊碳夹g(shù)并不容易。
在我的技術(shù)生涯中,有兩個(gè)坎讓我最感頭疼。
一個(gè)坎是Dos向Windows開發(fā)的變遷過程中,框架的概念,很長(zhǎng)一段時(shí)間我都理解不了。Dos時(shí)代,都是對(duì)函數(shù)庫(kù)的調(diào)用,你的程序主動(dòng)調(diào)用函數(shù)。Windows時(shí)代,則換成了框架。就算是你的main程序,其實(shí)也是被框架調(diào)用的。UI線程會(huì)從操作系統(tǒng)獲取消息,然后發(fā)送給你的程序來處理。Java程序員熟悉的Spring框架,也是這樣一個(gè)反向調(diào)用的框架。
現(xiàn)在因?yàn)?ldquo;框架”這個(gè)術(shù)語顯得很高大上,因此很多“類庫(kù)”/“函數(shù)庫(kù)”都自稱為“框架”。在我看來這都是名稱的濫用。
“類庫(kù)”/“函數(shù)庫(kù)”就是我寫的代碼調(diào)用它們。
“框架”就是我注冊(cè)回調(diào)函數(shù)到框架,框架來調(diào)用我寫的函數(shù)。
另一個(gè)坎就是面向?qū)ο?。很長(zhǎng)一段時(shí)間我都不知道應(yīng)該怎么設(shè)計(jì)類和類之間的關(guān)系,不能很好的設(shè)計(jì)出類層次結(jié)構(gòu)來。
我記得當(dāng)時(shí)看到一本外國(guó)大牛的書,他講了一個(gè)很簡(jiǎn)單、很實(shí)用的面向?qū)ο笤O(shè)計(jì)技巧:“敘述問題。然后把其中的名詞找出來,用來構(gòu)建類。把其中的動(dòng)詞找出來,用來構(gòu)建類的方法”。雖然這個(gè)技巧挺管用的,但也太草根了點(diǎn),沒有理論依據(jù),也不嚴(yán)謹(jǐn)。如果問題敘述的不好,那么獲得的類系統(tǒng)就會(huì)是有問題的。
掌握面向?qū)ο笏枷氲耐緩綉?yīng)該有很多種,我是從關(guān)系數(shù)據(jù)庫(kù)中獲得了靈感來理解和掌握面向?qū)ο笤O(shè)計(jì)思想的。
在我看來,關(guān)系數(shù)據(jù)庫(kù)的表,其實(shí)就是一個(gè)類,每一行記錄就是一個(gè)類的實(shí)例,也就是對(duì)象。表之間的關(guān)系,就是類之間的關(guān)系。O-Rmapping技術(shù)(如Hibernate),用于從面向?qū)ο蟠a到數(shù)據(jù)庫(kù)表之間的映射,這也說明了類和表確實(shí)是邏輯上等價(jià)的。
既然數(shù)據(jù)庫(kù)設(shè)計(jì)和類設(shè)計(jì)是等價(jià)的,那么要設(shè)計(jì)面向?qū)ο笙到y(tǒng),只需要使用關(guān)系數(shù)據(jù)庫(kù)的設(shè)計(jì)技巧即可。
關(guān)系數(shù)據(jù)庫(kù)表結(jié)構(gòu)設(shè)計(jì)是很簡(jiǎn)單的:
1識(shí)別表和表之間的關(guān)系,也就是類和類之間的關(guān)系。是一對(duì)一,一對(duì)多,多對(duì)一,還是多對(duì)多。這就是類之間的關(guān)系。
2識(shí)別表的字段。一個(gè)對(duì)象當(dāng)然有無數(shù)多的屬性(如,人:身高,體重,性別,年齡,姓名,身份證號(hào),駕駛證號(hào),銀行卡號(hào),護(hù)照號(hào),港澳通行證號(hào),工號(hào),病史,婚史etc),我們寫程序需要記錄的只是我們關(guān)心的屬性。這些關(guān)心的屬性,就是表的字段,也就是類的屬性。“弱水三千,我取一瓢飲”!
4段—設(shè)計(jì)模式:
曾經(jīng)在網(wǎng)上看到這樣一句話:“沒有十萬行代碼量,就不要跟我談什么設(shè)計(jì)模式”。深以為然。
記得第一次看Gof的設(shè)計(jì)模式那本書的時(shí)候,發(fā)現(xiàn)雖然以前并不知道設(shè)計(jì)模式,但在實(shí)際編程過程中,其實(shí)還是自覺使用了一些設(shè)計(jì)模式。設(shè)計(jì)模式是編程的客觀規(guī)律,不是誰發(fā)明的,而是一些早期的資深程序員首先發(fā)現(xiàn)的。
不用設(shè)計(jì)模式,你也可以寫出滿足需求的程序來。但是,一旦后續(xù)需求變化,那么你的程序沒有足夠的柔韌性,將難以為繼。而真實(shí)的程序,交付客戶后,一定會(huì)有進(jìn)一步的需求反饋。而后續(xù)版本的開發(fā),也一定會(huì)增加需求。這是程序員無法回避的現(xiàn)實(shí)。
寫UI程序,不論是Web,Desktop,Mobile,Game,一定要使用MVC設(shè)計(jì)模式。否則你的程序面對(duì)后續(xù)變化的UI需求,將無以為繼。
設(shè)計(jì)模式,最重要的思想就是解耦,通過接口來解耦。這樣,如果將來需求變化,那么只需要提供一個(gè)新的實(shí)現(xiàn)類即可。
主要的設(shè)計(jì)模式,其實(shí)都是面向?qū)ο蟮?。因此,可以認(rèn)為設(shè)計(jì)模式是面向?qū)ο蟮母呒?jí)階段。只有掌握了設(shè)計(jì)模式,才能認(rèn)為是真正徹底掌握了面向?qū)ο笤O(shè)計(jì)技巧。
我學(xué)習(xí)一門新語言時(shí)(包括非面向?qū)ο笳Z言,如函數(shù)式編程語言),總是會(huì)在了解了其語法后,看一下各類設(shè)計(jì)模式在這門語言中是如何實(shí)現(xiàn)的。這也是學(xué)習(xí)編程語言的一個(gè)竅門。
5段--語言專家:
經(jīng)過一段時(shí)間的編程實(shí)踐,程序員對(duì)某一種常用的編程語言已經(jīng)相當(dāng)精通了。有些人還成了“語言律師”,擅長(zhǎng)向其他程序員講解語言的用法和各種坑。
這一階段的程序員,常常是自己所用語言的忠實(shí)信徒,常在社區(qū)和論壇上和其他語言的使用者爭(zhēng)論哪一種語言是最好的編程語言。他們認(rèn)為自己所用的語言是世界上最好的編程語言,沒有之一。他們認(rèn)為,自己所用的編程語言適用于所有場(chǎng)景。他們眼中,只有錘子,因此會(huì)把所有任務(wù)都當(dāng)成是釘子。
6段--多語言專家:
這一個(gè)階段的程序員,因?yàn)楣ぷ麝P(guān)系,或者純粹是因?yàn)閷?duì)技術(shù)的興趣,已經(jīng)學(xué)習(xí)和掌握了好幾種編程語言。已經(jīng)領(lǐng)略了不同編程語言不同的設(shè)計(jì)思路,對(duì)每種語言的長(zhǎng)處和短處有了更多的了解。
他們現(xiàn)在認(rèn)為,編程語言并不是最重要的,編程語言不過是基本功而已。
他們現(xiàn)在會(huì)根據(jù)不同的任務(wù)需求,或者不同的資源來選擇不同的編程語言來解決問題,不再會(huì)因?yàn)闆]有使用某一種喜愛的編程語言開發(fā)而埋怨。
編程語言有很多種流派和思想,有一些編程語言同時(shí)支持多種編程范式。
靜態(tài)類型編程范式
采用靜態(tài)類型編程范式的編程語言,其變量需要明確指定類型。代表語言:C,C++,Pascal,Objective-C,Java,C#,VB.NET,Swif,Golang。
這樣做的好處是:
1,編譯器可以在編譯時(shí)就能找出類型錯(cuò)誤。
2,編譯器編譯時(shí)知道類型信息,就可以提高性能。
這種范式認(rèn)為,程序員肯定知道變量的類型,你丫要是不知道變量的類型,那你就別混了!編譯時(shí),程序會(huì)報(bào)錯(cuò)。
Swift和Go語言都是靜態(tài)類型編程語言,但它們都不需要明確指定類型,而是可以通過推斷由編譯器自動(dòng)確定其類型。
動(dòng)態(tài)類型編程范式
采用靜態(tài)類型編程范式的編程語言,其變量不需要明確指定類型。任意變量,可以指向任意類型的對(duì)象。代表語言:Python,Ruby,JavaScript。
動(dòng)態(tài)類型的哲學(xué)可以用鴨子類型(英語:ducktyping)這個(gè)概念來概括。JamesWhitcombRiley提出的鴨子測(cè)試可以這樣表述:“當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。”
這種范式認(rèn)為,程序員肯定知道變量的類型和它支持的方法和屬性,你丫要是不知道變量的類型,那你就別混了!運(yùn)行時(shí)程序會(huì)崩潰!程序崩潰怨誰?怨你自己?jiǎn)h,你不是合格的程序員!
動(dòng)態(tài)類型的好處是:
不需要明確定義接口和抽象類型。只要一個(gè)類型支持需要的方法和屬性,那么就OK。程序會(huì)相當(dāng)靈活和簡(jiǎn)單。C++,Java,C#視之為命脈的接口/基類,在動(dòng)態(tài)語言這里都視如無物!
缺點(diǎn)是:
1,如果類型不對(duì),編譯器也無法找到錯(cuò)誤,而是運(yùn)行時(shí)程序崩潰。
2,因?yàn)榫幾g器不知道變量的類型,因此無法優(yōu)化性能。
面向?qū)ο缶幊谭妒?/strong>
面向?qū)ο缶幊谭妒?,從上世紀(jì)70年代末開始興起。它支持類和類的實(shí)例作為封裝代碼的模塊。代表語言:Smalltalk,C++,Objective-C,Java,C#,VB.NET,Swift,Go,Python,Ruby,ActionScritp,OCaml.
早期編程語言都是面向過程的。就是順序,條件,循環(huán),構(gòu)成一個(gè)個(gè)函數(shù)。隨著代碼規(guī)模的增大,人們發(fā)現(xiàn)有必要對(duì)代碼進(jìn)行模塊化。一個(gè)概念對(duì)應(yīng)的代碼放在一個(gè)文件中,這樣便于并發(fā)開發(fā)和進(jìn)行代碼管理。
人們還發(fā)現(xiàn)了“程序=數(shù)據(jù)結(jié)構(gòu)+算法”的規(guī)律。因此,一個(gè)概念對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)和函數(shù)應(yīng)該放在一個(gè)文件中。這就是類的概念。
面向?qū)ο缶幊谭妒?,確實(shí)極大地提高了生產(chǎn)效率,因此得到了廣泛的應(yīng)用,因此在語言層面支持面向?qū)ο缶幊谭妒降恼Z言是極多的。
C語言盡管在語言層面上并不支持面向?qū)ο缶幊谭妒?,但現(xiàn)代的C語言開發(fā)都會(huì)應(yīng)用面向?qū)ο蟮哪K化思想,把同一類的數(shù)據(jù)結(jié)構(gòu)和函數(shù)放在一個(gè)文件中,采用類似的命名方式。
畢竟C語言沒有在語言層面上支持面向?qū)ο螅虼司陀泻芏喑绦騿T想給C語言添加面向?qū)ο笾С?。其中的代表是C++和Objective-C。
C++是一種新的語言,但大部分語言元素是和C兼容的。
Objective-C是完全兼容的C的。Objective-C是給C添加了薄薄的一層語法糖以支持接口(就是其他語言的類)和協(xié)議(就是其他語言的接口)。甚至,Objective-C一開始的實(shí)現(xiàn),就是一個(gè)C語言的預(yù)編譯器。Objective-C坦白講,除了添加的語法不太符合C流外,實(shí)際上其面向?qū)ο笙到y(tǒng)設(shè)計(jì)是相當(dāng)精妙的。喬布斯早年慧眼識(shí)珠,把Objective-C收人囊中,因?yàn)榉忾]于Apple/NextStep系統(tǒng)內(nèi),因此少有人知。隨著iOs系統(tǒng)的普及,Objective-C近幾年才名滿天下。
函數(shù)式編程范式
函數(shù)式編程范式,是一些數(shù)學(xué)家發(fā)明的編程語言,他們認(rèn)為程序就是數(shù)學(xué)函數(shù)嘛。代表語言:Lisp,Erlang,JavaScript,OCaml,Prog。
有很多大牛極力鼓吹過函數(shù)式編程語言,認(rèn)為其極具革命性。但我認(rèn)為他們過高估計(jì)了函數(shù)式編程范式的威力,我并不認(rèn)為函數(shù)式編程范式相對(duì)于面向?qū)ο缶幊谭妒接泻胃呙髦帯?/p>
函數(shù)式編程語言,核心就是函數(shù),它們沒有Class類的概念。但它的函數(shù)又不是傳統(tǒng)面向過程語言的函數(shù),它的函數(shù)支持“閉包”的概念。
在我看來,函數(shù)式編程語言的函數(shù),也就是“閉包”,說白了,其實(shí)就是“類”。編程語言發(fā)展到今天,就是需要模塊化,就是需要把“數(shù)據(jù)結(jié)構(gòu)”和“算法”結(jié)合起來。不論何種語言,不把它們結(jié)合起來的編程方式,都是沒有出路的。
面向?qū)ο缶幊陶Z言,用類把“數(shù)據(jù)結(jié)構(gòu)”和“算法”結(jié)合起來。類的核心是“數(shù)據(jù)結(jié)構(gòu)”,也就是其“屬性”,而不是“算法”,其“函數(shù)”。在類中,是函數(shù)依附于屬性。
而函數(shù)式編程語言,用閉包把“數(shù)據(jù)結(jié)構(gòu)”和“算法”結(jié)合起來。是函數(shù)能夠抓取外部的字段。是“屬性”依附于“函數(shù)”。
“類”本質(zhì)上和“閉包”是等價(jià)的?,F(xiàn)在很多面向?qū)ο缶幊陶Z言都加上了對(duì)閉包的支持。觀察其代碼,我們可以發(fā)現(xiàn),它們實(shí)際上都是用“類”來實(shí)現(xiàn)“閉包”的。
“類”和“閉包”誰更易用?明顯是“類”。
而“閉包”更簡(jiǎn)潔一些,因此“閉包”在面向?qū)ο缶幊陶Z言中常用來替換匿名類。只有一個(gè)函數(shù)的類,寫成一個(gè)類太麻煩,不如寫成閉包,更加簡(jiǎn)潔。
吐槽一下OCaml語言,其前身Caml語言本身是一種挺好的函數(shù)式語言,硬生生添加了一套完整的面向?qū)ο髾C(jī)制,同時(shí)支持面向?qū)ο蠛秃瘮?shù)式編程范式,很容易像C++一樣腦裂的。
也有很多面向?qū)ο笳Z言控看著JavaScript嫌煩,總是想把面向?qū)ο笾С痔砑拥絁avaScript上。ActionScript就是其中一種嘗試。我用過,真的是和Java沒多少區(qū)別了。
再吐槽一下ExtJS。當(dāng)初選型Web前端開發(fā)框架時(shí)比較了ExtJS和JQuery。
ExtJS明顯是Java高手開發(fā)的,硬生生用JavaScript模擬Swing的設(shè)計(jì)思想,搞了一套UI庫(kù)。
JQuery開發(fā)者明顯是領(lǐng)悟了JavaScript的函數(shù)式編程范式,依據(jù)JavaScript的動(dòng)態(tài)函數(shù)式編程語言的特點(diǎn)打造了一套UI庫(kù),立刻秒殺ExtJS。
由ExtJS和JQuery的故事,我們可以看到多語言編程能力是多么的重要。ExtJS的作者精通并喜愛Java,因此他把手術(shù)刀JavaScript當(dāng)做錘子Java使,一通亂敲,費(fèi)力不討好。
函數(shù)式編程語言,還有尾遞歸等一些小技巧。尾遞歸可以不用棧,防止遞歸調(diào)用時(shí)棧溢出。
模板編程范式
模板編程,就是把類型作為參數(shù),一套函數(shù)可以支持任意多種類型。代表語言:C++。
模板編程的需求,是在C++開發(fā)容器庫(kù)的時(shí)候發(fā)明的。因?yàn)槿萜餍枰4嫒我忸愋偷膶?duì)象,因此就有了泛型的需求。
C++的模板編程,是在編譯時(shí),根據(jù)源碼中的使用情況,創(chuàng)建對(duì)應(yīng)類型的代碼。除了C++這種方式,Java,C#也有類似的機(jī)制,叫做“泛型”,但它們的實(shí)現(xiàn)方式和C++的模板很不同。它們的編譯器不會(huì)生成新的代碼,而是使用強(qiáng)制類型轉(zhuǎn)換的方式實(shí)現(xiàn)。
在沒有模板/泛型的編程語言中,怎樣在容器中存放對(duì)象呢?存取公共基類類型(Java,C#)的對(duì)象,或者void*指針(C)即可,取出時(shí)自己強(qiáng)制類型轉(zhuǎn)換為實(shí)際類型。動(dòng)態(tài)類型語言,不關(guān)心類型,更是無所謂了,隨便什么對(duì)象直接往容器里扔進(jìn)去,取出來直接用即可。
一些C++高手又在模板的基礎(chǔ)上搞出了“模板元編程”。因?yàn)槟0寰幊?,就是C++的編譯器搞定的嘛,模板元編程就是讓編譯器運(yùn)算,編譯完結(jié)果也就算出來了。我不知道除了研究和炫技,這玩意有啥用?
小結(jié)
一門語言是否值得學(xué)習(xí),我認(rèn)為有幾個(gè)標(biāo)準(zhǔn):
1,是否要用,要用就得學(xué),這么沒有疑問的。畢竟我們都要吃飯的嘛。
2,其語言特性是否給你耳目一新的感覺。如果是,那就值回票價(jià)了。如Go語言廢掉了異常,改用返回多值。我深以為然。我其實(shí)已經(jīng)主動(dòng)不用異常好多年了。因?yàn)?,我覺得既然C不支持異常也活得很好,為什么需要異常呢?出錯(cuò)了,返回錯(cuò)誤碼。無法挽回的錯(cuò)誤,直接Abort程序就可以嘛!而且,異常實(shí)際上是違反面向過程編程原則的。一個(gè)函數(shù)應(yīng)該只有一個(gè)入口一個(gè)出口。拋出異常就多了出口了。
3,是否擅長(zhǎng)某一個(gè)領(lǐng)域。如果你手里只有一把錘子,那么你就只能把所有任務(wù)都當(dāng)做釘子猛錘一通。但如果工具箱里有多種工具,那面對(duì)不同的任務(wù)就得心應(yīng)手多了。
7段—架構(gòu)設(shè)計(jì)
還需要掌握架構(gòu)設(shè)計(jì)的能力,才能設(shè)計(jì)出優(yōu)秀的軟件。架構(gòu)設(shè)計(jì)有一些技巧:
1,分層
一個(gè)軟件通常分為:
表現(xiàn)層--UI部分
接口層--后臺(tái)服務(wù)的通訊接口部分
服務(wù)層--實(shí)際服務(wù)部分
存儲(chǔ)層—持久化存儲(chǔ)部分,存儲(chǔ)到文件或者數(shù)據(jù)庫(kù)。
分層的軟件,可以解耦各個(gè)模塊,支持并行開發(fā),易于修改,易于提升性能。
2,SOA
模塊之間通過網(wǎng)絡(luò)通訊互相連接,松耦合。每一個(gè)模塊可以獨(dú)立部署,可以增加部署實(shí)例從而提高性能。每一個(gè)模塊可以使用不同的語言和平臺(tái)開發(fā),可以重用之前開發(fā)的服務(wù)。SOA,常用協(xié)議有WebService,REST,JSON-RPC等。
3,性能瓶頸
1)化同步為異步。
用內(nèi)存隊(duì)列(Redis),工作流引擎(JBpm)等實(shí)現(xiàn)。內(nèi)存隊(duì)列容易丟失數(shù)據(jù),但是速度快。工作流引擎會(huì)把請(qǐng)求保存到數(shù)據(jù)庫(kù)中。
通過化同步請(qǐng)求為異步請(qǐng)求,基本上99.99%的性能問題都可以解決。
2)用單機(jī)并行硬件處理。
如,使用GPU,F(xiàn)PGA等硬件來處理,提高性能。
3)用集群計(jì)算機(jī)來處理。
如,Hadoop集群,用多臺(tái)計(jì)算機(jī)來并行處理數(shù)據(jù)。
自己的軟件棧中,也可以把一個(gè)模塊部署多份,并行處理。
4)用cache來滿足請(qǐng)求。常用的內(nèi)容加熱cache后,大量的用戶請(qǐng)求都只是內(nèi)存讀取數(shù)據(jù)而已,性能會(huì)得到很大的提升。
cache是上帝算法,記得好像它的性能只比最佳性能低一些,就好像你是上帝,能夠預(yù)見未來一樣?,F(xiàn)在X86CPU遇到了主頻限制,CPU提升性能的主要途徑就是增加高速Cache了。
4,大系統(tǒng)小做
遇到大型系統(tǒng)不要慌,把它切分成多個(gè)模塊,用多個(gè)小程序,通過SOA協(xié)作來解決。這秉承了Unix的設(shè)計(jì)思想。Unix上開發(fā)了大量單一目的的小程序,它主張用戶通過管道來讓多個(gè)小程序協(xié)作,解決用戶的需求。當(dāng)然,管道方式通訊限制太多,不夠靈活。因此,現(xiàn)在我們可以通過URI,通過SOA的方式來讓多個(gè)程序協(xié)作。Andorid和iOS上的應(yīng)用程序,現(xiàn)在都是通過URI實(shí)現(xiàn)協(xié)作的。這也算是Unix設(shè)計(jì)思想的現(xiàn)代發(fā)展吧?!
5,Sharding切片
現(xiàn)在有一個(gè)潮流,就是去IOE。I-IBM大型機(jī),O-Oracle數(shù)據(jù)庫(kù),E-EMC存儲(chǔ)。之前,大型系統(tǒng)常用IOE去架構(gòu),在大型機(jī)上部署一個(gè)Oracle數(shù)據(jù)庫(kù),Oracle數(shù)據(jù)庫(kù)用EMC存儲(chǔ)保存數(shù)據(jù)。IOE是當(dāng)今最強(qiáng)的計(jì)算機(jī),數(shù)據(jù)庫(kù)和存儲(chǔ)。但他們面對(duì)海量系統(tǒng)也有抗不住的一天。
Oracle數(shù)據(jù)庫(kù)是Shareeverything的,它可以在一個(gè)計(jì)算機(jī)集群(服務(wù)器節(jié)點(diǎn)不能超過16個(gè))上運(yùn)行。計(jì)算機(jī)集群都共用一個(gè)存儲(chǔ)。
去IOE運(yùn)動(dòng),標(biāo)志著ShareEverything模式的破產(chǎn)。必須使用ShareNothing,系統(tǒng)才能無限擴(kuò)展。
用MySQL數(shù)據(jù)庫(kù)就可以應(yīng)付任意規(guī)模的數(shù)據(jù)了。前提是,你會(huì)Sharding分片。把大系統(tǒng)切分成若干個(gè)小系統(tǒng),切分到若干臺(tái)廉價(jià)服務(wù)器和存儲(chǔ)上。更Modern一些,就是切分到大量虛擬機(jī)上。
如,鐵道部的12306網(wǎng)站。我們知道火車票都是從屬于某一列列車的。那么我們把每一個(gè)列車作為一個(gè)單元來切分,就可以把12306網(wǎng)站切分成幾千個(gè)模塊。一臺(tái)虛擬機(jī)可以承載若干個(gè)模塊。當(dāng)某些列車成為性能瓶頸之后,就可以把它們遷移到獨(dú)立的虛擬機(jī)上。即使最終有部分列出服務(wù)不可用,系統(tǒng)也不會(huì)完全不可用。
12306網(wǎng)站,只有一個(gè)全局的部分,就是用戶登錄。這個(gè)可以交給第三方負(fù)責(zé)。如可以讓用戶用微信,微博,qq等賬戶登錄。
也可以自己實(shí)現(xiàn)用戶登錄服務(wù)。還是用切片的方式用多臺(tái)Redis服務(wù)器提供服務(wù)。Redis服務(wù)器存儲(chǔ)每一個(gè)登錄用戶的sessionId和userId,角色,權(quán)限等信息。sessionId是隨機(jī)生成的,可選擇其部分bit用于標(biāo)識(shí)它在哪一個(gè)Redis服務(wù)器上。用戶登錄后,把sessionId發(fā)給客戶。用戶每次請(qǐng)求時(shí)把sessionId發(fā)回給服務(wù)器。服務(wù)器把sessionId發(fā)給Redis服務(wù)器查詢得到其用戶信息,對(duì)用戶請(qǐng)求進(jìn)行處理。如果在redis服務(wù)器上找不到sessionId,則讓用戶去登錄。即使所有注冊(cè)用戶同時(shí)登陸,也不需要太多的內(nèi)存。而且,可以在session內(nèi)存過多時(shí),刪除最早登陸的用戶的session,強(qiáng)制他再次登陸。同時(shí)活躍的用戶數(shù)不會(huì)太多。
領(lǐng)域知識(shí)層次
前面的所有層次,都是關(guān)注編程本身的技能,說白了,就是基本功,本身并不能產(chǎn)生太大的價(jià)值。但有太多的程序員浪費(fèi)太多的時(shí)間在那些筑基的層次上。
有些程序員特別喜歡鉆研編程語言,每有一種新的編程語言出來或者舊語言被熱炒,就會(huì)投入精力進(jìn)去研究。我就是其中之一,浪費(fèi)了很多精力在編程語言上,在奇技淫巧上。
我覺得C++語言是一個(gè)特別大的坑。剛開始是作為面向?qū)ο蟮腃被開發(fā)的。后來發(fā)現(xiàn)了模板編程,就大力鼓吹模板編程和進(jìn)一步的模板元編程。最近又推出了C++11,C++14等新標(biāo)準(zhǔn),進(jìn)一步添加了很多新東西,函數(shù)式編程,類型推斷等。C++過分復(fù)雜,太多的坑消耗了大量程序員的大量精力。我使用C++時(shí),只使用面向?qū)ο蟛糠趾湍0宀糠?,其他過于精深的特性都不使用。
計(jì)算機(jī)科學(xué)是一個(gè)面相當(dāng)廣泛的學(xué)科,有很多領(lǐng)域知識(shí)需要和值得我們深入研究,我們才能寫出有價(jià)值的程序來。軟件必須要和行業(yè)結(jié)合起來,要落地才有價(jià)值。僅僅研究編程技巧,不懂領(lǐng)域知識(shí)是寫不出有價(jià)值的程序的。
計(jì)算機(jī)科學(xué)領(lǐng)域有很多,列舉一些如下:
存儲(chǔ)----塊設(shè)備,文件系統(tǒng),集群文件系統(tǒng),分布式文件系統(tǒng),光纖SCSI,iSCSI,RAID等。
網(wǎng)絡(luò)----以太網(wǎng),光纖網(wǎng),蜂窩網(wǎng)絡(luò),WIFI,VLAN等。
計(jì)算機(jī)體系結(jié)構(gòu),主要就是CPU指令集。x86,ARM等。
USB協(xié)議。需要知道URB包。
PCI協(xié)議,PCI-E協(xié)議?,F(xiàn)代計(jì)算機(jī)的外設(shè)都是PCI協(xié)議和PCI-E協(xié)議的。顯卡現(xiàn)在全是通過 PCI-E協(xié)議連接到計(jì)算機(jī)上的。相對(duì)來說減少了很多需要學(xué)習(xí)的知識(shí)。搞虛擬化就需要深入掌握PCI協(xié)議。
圖像處理--圖像壓縮,視頻實(shí)時(shí)編碼等。
3D游戲
關(guān)系數(shù)據(jù)庫(kù)
NoSQL數(shù)據(jù)庫(kù)
操作系統(tǒng)
分布式操作系統(tǒng)
編譯原理
機(jī)器學(xué)習(xí)--現(xiàn)在大數(shù)據(jù)要用哦!
了解這些領(lǐng)域知識(shí),也包括了解該領(lǐng)域現(xiàn)有的商用硬件、商用軟件和開源軟件。很多時(shí)候,你要完成的工作,已經(jīng)有現(xiàn)成的工具了。你只要使用現(xiàn)成的工具就可以完成任務(wù),不需要進(jìn)行開發(fā)。有時(shí)候,只需要組合現(xiàn)有的工具,寫一些腳本就可以完成任務(wù)。
如,我一次要實(shí)現(xiàn)一個(gè)雙向同步任務(wù)。找到了一個(gè)優(yōu)秀的開源軟件Unison,編寫一下配置文件就圓滿地完成了任務(wù)。不需要編寫任何代碼。
還有一次,要做高可用,用Python調(diào)用了幾個(gè)開源軟件就輕松實(shí)現(xiàn)了。
編寫安裝程序,定制操作系統(tǒng),知道了操作系統(tǒng)的領(lǐng)域知識(shí),寫幾行腳本就可以輕松搞定。
不具備領(lǐng)域知識(shí)的人,就可能不得不進(jìn)行大量無謂的開發(fā),甚至開發(fā)很久之后才發(fā)現(xiàn),這根本就是一條死路。
另外,扎實(shí)的領(lǐng)域知識(shí),可以大大提高編程調(diào)試、查錯(cuò)的能力。知道編譯器和編程語言運(yùn)行時(shí)工作原理,就能快速根據(jù)編譯錯(cuò)誤和警告信息修改代碼。
知道操作系統(tǒng)底層運(yùn)行機(jī)制,就能快速找到運(yùn)行時(shí)錯(cuò)誤的問題根源。如,有一次我編寫一個(gè)windows升級(jí)服務(wù)程序。它是一個(gè)windows服務(wù),需要執(zhí)行dos腳本,這個(gè)腳本會(huì)替換掉這個(gè)windows服務(wù)本身。發(fā)現(xiàn)有時(shí)腳本執(zhí)行無效,查了一晚上,發(fā)現(xiàn)當(dāng)windows服務(wù)安裝后,第一次啟動(dòng)就執(zhí)行腳本時(shí)就會(huì)有權(quán)限問題,log都正確,但實(shí)際執(zhí)行這個(gè)腳本沒有任何效果。但一旦windows服務(wù)程序啟動(dòng)一次之后就ok。這必然是windows操作系統(tǒng)底層安全機(jī)制的問題,因?yàn)槲覍?duì)Windows內(nèi)核了解不多,因此花了很長(zhǎng)時(shí)間才發(fā)現(xiàn)這個(gè)問題,并對(duì)造成這個(gè)問題的根源并不清楚。
0段—領(lǐng)域知識(shí)菜鳥
對(duì)領(lǐng)域知識(shí)沒有多少認(rèn)知,通過搜索引擎找到一些該領(lǐng)域的軟件和硬件的介紹性文章,按照文章指示配置和使用軟件。勉強(qiáng)能夠使用現(xiàn)有軟硬件。
1段—領(lǐng)域知識(shí)行家
了解領(lǐng)域內(nèi)常用硬件,深入掌握領(lǐng)域內(nèi)常用軟件的配置和使用技巧。能夠使用現(xiàn)有軟硬件熟練搭建解決方案,能夠解決實(shí)際工作中遇到的種種問題。
2段—領(lǐng)域知識(shí)專家
當(dāng)你不僅僅掌握了該領(lǐng)域的軟件和工具,知道怎么用,還知道其原理,“知其然,也知其所以然”,就是該領(lǐng)域的知識(shí)專家了。
你知道網(wǎng)絡(luò)協(xié)議的原理,你才能在網(wǎng)絡(luò)出現(xiàn)問題時(shí)知道是哪里可能出現(xiàn)了問題。是mac沖突,ip沖突,還是網(wǎng)絡(luò)環(huán)路?
你知道存儲(chǔ)的原理,你才能知道為什么這種存儲(chǔ)方式不適合虛擬化,那種存儲(chǔ)方式適合虛擬化,另一種方式適合資料備份。
你知道PCI協(xié)議,你才能知道你怎樣才能虛擬化一個(gè)硬件設(shè)備。
你知道網(wǎng)卡硬件協(xié)議,你才能模擬出一個(gè)虛擬機(jī)能正常使用的虛擬網(wǎng)卡。
你知道視頻編碼格式和原理,才能知道什么視頻格式占用帶寬最少,什么視頻格式占用CPU最少。
你了解IntelVT/Amd V指令集,才能知道虛擬化是怎樣實(shí)現(xiàn)的。
你明白工作流其實(shí)就是狀態(tài)機(jī),在遇到復(fù)雜工作流程時(shí),你才能知道怎樣設(shè)計(jì)滿足要求的工作流引擎。
3段—科學(xué)家
你是領(lǐng)域知識(shí)專家,但你的知識(shí)都是來自于書本,來自于其他人的。
如果你滿足于當(dāng)領(lǐng)域知識(shí)專家,你只能拾人牙慧,永遠(yuǎn)別想超越。別人的研究成果,未必愿意告訴你。當(dāng)別人告訴你的時(shí)候,它可能已經(jīng)發(fā)現(xiàn)了更新的理論,并且新一代產(chǎn)品可能馬上就要發(fā)布了。
科學(xué)家是探索未知,勇于創(chuàng)新的人,是推動(dòng)人類社會(huì)進(jìn)步的人。
傳說,思科的一位高管曾經(jīng)半開玩笑地說過:“如果思科停止了新技術(shù)的研發(fā),華為就會(huì)找不著方向”。這是在嘲笑華為只是處在領(lǐng)域知識(shí)專家的水平,只能山寨無法超越。我不知道華為的實(shí)際情況,但希望現(xiàn)在的華為已經(jīng)走到了領(lǐng)跑者的位置。
歐文·雅各布斯發(fā)現(xiàn)了CDMA碼分多址的原理,并發(fā)現(xiàn)它在通訊上大有可為,組建了高通公司。高通公司主要以專利授權(quán)費(fèi)為生,它雇傭了大量科學(xué)家在通訊領(lǐng)域展開研究。有人說高通是專利流氓。這些人不明白知識(shí)的價(jià)值。在他們眼里,Windows的合理價(jià)格就應(yīng)該是5元錢,一張光盤的價(jià)格。iPhone就應(yīng)該是1000多元裸機(jī)的價(jià)格。高通是專利流氓,那你也流氓一個(gè)CDMA,LTE出來給我看看!
X86芯片在設(shè)計(jì)上沒有考慮虛擬化。因此會(huì)有所謂的“虛擬化漏洞”出現(xiàn)。就是說,一些CPU特權(quán)指令執(zhí)行時(shí),在虛擬機(jī)環(huán)境下不會(huì)拋出異常,因此就無法切換到Host。這樣,X86芯片上就無法運(yùn)行虛擬機(jī)。
VmWare公司是由美國(guó)的幾位科學(xué)家在1998年創(chuàng)建的。他們發(fā)現(xiàn)可以使用二進(jìn)制翻譯的技術(shù),在X86計(jì)算機(jī)上運(yùn)行虛擬機(jī)。
Xen虛擬化軟件也是幾位科學(xué)家發(fā)明的。他們發(fā)現(xiàn)只要修改虛擬機(jī)操作系統(tǒng)和Host操作系統(tǒng)的內(nèi)核,在需要執(zhí)行“虛擬化漏洞”指令時(shí)直接調(diào)用Host的功能,就可以實(shí)現(xiàn)虛擬化,而且大大提高了虛擬機(jī)的運(yùn)行性能。
后來,Intel為自己的芯片添加了IntelVT指令集,Amd為自己的芯片添加了AmdV指令集,彌補(bǔ)了“虛擬化漏洞”。于是就有了KVM虛擬機(jī)軟件,它直接用CPU硬件指令實(shí)現(xiàn)虛擬化。
KVM在執(zhí)行CPU指令時(shí),是直接在物理CPU上運(yùn)行的,因此效率極高。但是,虛擬機(jī)運(yùn)行虛擬外設(shè)時(shí),就必須用軟件模擬,因此虛擬機(jī)的IO訪問速度很慢。
IBM科學(xué)家RustyRussell,借鑒了Xen的研發(fā)經(jīng)驗(yàn),創(chuàng)建了VirtIO技術(shù)。就是在虛擬機(jī)中編寫一套PCI虛擬設(shè)備和驅(qū)動(dòng),這套虛擬PCI設(shè)備有一塊虛擬設(shè)備內(nèi)存。這個(gè)虛擬設(shè)備內(nèi)存Host是可以訪問的,虛擬機(jī)通過VirtIO驅(qū)動(dòng)程序也可以訪問。也就是一塊內(nèi)存在虛擬機(jī)和Host中共享,這就解決了虛擬機(jī)的IO性能問題。
再講一個(gè)搜索引擎的故事:
很久以前,我要給一個(gè)程序添加搜索功能。剛開始使用sql查詢實(shí)現(xiàn),發(fā)現(xiàn)實(shí)在太慢了。后來找了開源的Lucene項(xiàng)目。它使用反向索引技術(shù),通過在文件中創(chuàng)建反向索引,大大提高了搜索速度。
Google的兩位創(chuàng)始人發(fā)現(xiàn)了html中l(wèi)ink的秘密,他們發(fā)現(xiàn)可以通過html頁面的link關(guān)系來為每一個(gè)html頁面設(shè)置權(quán)重。也就是PageRank算法。于是,Google的自動(dòng)搜索引擎擊敗了Yahoo人工分類的搜索引擎。
OK,利用反向索引技術(shù)和PageRank,以及一個(gè)簡(jiǎn)單的html爬蟲機(jī)器人,我們就可以創(chuàng)建一個(gè)搜索引擎了。但是,互聯(lián)網(wǎng)很大,每天產(chǎn)生大量新網(wǎng)頁,要為整個(gè)互聯(lián)網(wǎng)建立反向索引是很困難的。
若干年后Google又公開了三篇論文:Googlefs,Mapreduce,Bigtable。于是Lucene項(xiàng)目的開發(fā)者根據(jù)Google的Mapreduce論文開發(fā)了Hadoop項(xiàng)目。MapReduce就是使用大量計(jì)算機(jī)存儲(chǔ)數(shù)據(jù)并計(jì)算,最后匯總結(jié)果。使用Hadoop+反向索引+PageRank,就可以創(chuàng)建搜索引擎了。Yahoo,Baidu等公司紛紛基于Hadoop開發(fā)了自己的搜索引擎。
但是,其他公司的搜索引擎效果還是沒法和Google相比。這一點(diǎn)我們程序員最清楚。像我,就總是翻墻出去,只為了Google一下。
Google黑板報(bào)上發(fā)表了吳軍博士的一些文章,其中介紹了很多機(jī)器學(xué)習(xí)方面的知識(shí)。從文中可以知道,Google其實(shí)使用機(jī)器學(xué)習(xí)來分析搜集到的頁面。Google明顯不會(huì)把這個(gè)公式公開出來。即使有一天Google真的公開了這個(gè)公式,那么可以想見Google肯定又研發(fā)出了更加犀利的秘籍,山寨貨的搜索引擎效果還是比不上Google的。
山寨是通向創(chuàng)新的必由之路。在成為領(lǐng)域的領(lǐng)頭羊和領(lǐng)導(dǎo)者之前,必然要經(jīng)過學(xué)習(xí),模仿的階段。但要成為行業(yè)的老大,成為Champion,必須勇于彎道超車,勇敢地走上創(chuàng)新之路,成為真正的科學(xué)家,真正的大牛!
總結(jié)
編程能力可分為兩個(gè)維度:一個(gè)是編程技能水平,另一個(gè)是領(lǐng)域知識(shí)水平。
有些程序員可能把精力都花在提升編程技能上了,領(lǐng)域知識(shí)知之甚少,這其實(shí)在日常工作中也是極其有害的。有些需求可能早已經(jīng)有了現(xiàn)成、開源免費(fèi)的解決方案,或者只需要組合幾個(gè)現(xiàn)有軟件就可以快速搞定,而他們卻不得不自己花大量時(shí)間去開發(fā)。另外,缺少領(lǐng)域知識(shí),在程序出現(xiàn)非預(yù)期狀況時(shí),很難快速定位到問題的根源,很難解決bug。