冒號(hào)和他的學(xué)生們(連載22)——抽象思維
冒號(hào)和他的學(xué)生們
——程序員提高班紀(jì)事
22.抽象思維
是謂無(wú)狀之狀,無(wú)物之象,是謂惚恍 ——《老子·道經(jīng)》
冒號(hào)健步走進(jìn)教室,學(xué)員們立刻正襟危坐,進(jìn)入戰(zhàn)備狀態(tài)。
“如果說(shuō)咱們是在合演一場(chǎng)戲,那么前面五節(jié)課只是一個(gè)過(guò)門(mén)。”冒號(hào)儼然一副自導(dǎo)自演的架勢(shì)。
眾人暗暗吃驚:這過(guò)門(mén)也忒長(zhǎng)了點(diǎn)吧。
冒號(hào)隨即探問(wèn):“還記得在范式總結(jié)中提到的迭代學(xué)習(xí)法吧?”
引號(hào)迅速應(yīng)答:“就是在具體知識(shí)與抽象理論之間做折返跑。”
“記性不錯(cuò)。”冒號(hào)贊道,“在上本班之前,你們已經(jīng)具備了一定的編程語(yǔ)言的基礎(chǔ),因此我們先從抽象的編程范式談起,此后又回歸到編程語(yǔ)言的討論。”
句號(hào)推測(cè):“照此邏輯,下面我們將再次返回編程范式?”
“我們的確要來(lái)個(gè)For Loop,但相信這是一個(gè)增量式的循環(huán)。”冒號(hào)用編程語(yǔ)言來(lái)強(qiáng)調(diào)他的學(xué)習(xí)理論。
逗號(hào)有些失望:“按計(jì)劃不是該對(duì)Java語(yǔ)言作專(zhuān)題討論嗎?”
“你放心,Java它跑不了。”冒號(hào)看出他的心事,“語(yǔ)言是形,范式是神,這次我們將二者融合,爭(zhēng)取做到形神兼?zhèn)洹>唧w地說(shuō),范式以OOP為主,語(yǔ)言以Java為主,同時(shí)可能涉及C、C++或C#等語(yǔ)言。另外,示例代碼也會(huì)明顯增多。”
逗號(hào)臉上的一抹烏云頓時(shí)消散開(kāi)來(lái)。
冒號(hào)接著提醒道:“不過(guò),秉承開(kāi)班發(fā)言中的理念,我們的重心不在知識(shí)的枝節(jié),而在知識(shí)的本源。因此無(wú)論討論Java還是OOP,我們不追求系統(tǒng)和全面,但力求從不同的選點(diǎn)、角度和深度來(lái)展示知識(shí)的活性。”
講到此處,冒號(hào)冷不丁提問(wèn):“程序員最重要的能力是什么?”
眾人的答案五花八門(mén):學(xué)習(xí)能力、邏輯思維能力、解決問(wèn)題能力、專(zhuān)注力、溝通能力等等。
“毫無(wú)疑問(wèn),你們所說(shuō)的都很重要。這本是個(gè)見(jiàn)仁見(jiàn)智的問(wèn)題,我只是借此展開(kāi)今后的話題。在我看來(lái),抽象思維能力是最重要的。當(dāng)然,不獨(dú)計(jì)算機(jī)領(lǐng)域,其他科學(xué)同樣需要這種能力。更廣泛地說(shuō),抽象是人類(lèi)認(rèn)識(shí)和描繪世界最首要的工具。”不知不覺(jué)冒號(hào)又走上了形而上的路線。
嘆號(hào)這時(shí)想起:“前面談切面范式時(shí),似乎專(zhuān)門(mén)提到過(guò)抽象與分解的重要性。”
“我們也曾提到,不同的范式正是對(duì)軟件進(jìn)行了不同角度的抽象和分解。”冒號(hào)加以補(bǔ)充,“那么什么是抽象呢?不妨概括為:去粗取精以化繁為簡(jiǎn);由表及里以異中求同。再精煉些,抽象就是作減法和除法。”
問(wèn)號(hào)半信半疑:“減法好理解,通過(guò)甄選減去非本質(zhì)和不重要的部分,即去粗取精。可除法呢?”
句號(hào)忽然悟道:“透過(guò)現(xiàn)象看本質(zhì),發(fā)現(xiàn)不同事物之間的相同之處,即異中求同。同類(lèi)歸并,那就是除法了。”
冒號(hào)進(jìn)一步解釋?zhuān)?#8220;用離散數(shù)學(xué)或抽象代數(shù)的語(yǔ)言來(lái)說(shuō),通過(guò)抽象而產(chǎn)生等價(jià)關(guān)系以及相應(yīng)的等價(jià)類(lèi),便是集合的商運(yùn)算。”
逗號(hào)嘀咕:“本來(lái)快明白了,經(jīng)這么一描述,重新糊涂了。”
冒號(hào)笑道:“如果嫌數(shù)學(xué)語(yǔ)言高深,就用算術(shù)語(yǔ)言吧。乘法可看作同類(lèi)復(fù)制,作為逆運(yùn)算的除法自然是同類(lèi)歸并了。”
逗號(hào)眼中的迷惘漸漸散去,若有所悟:“嗯,經(jīng)過(guò)減法和除法,大數(shù)變小數(shù),復(fù)雜變簡(jiǎn)單。”
“能否把抽象說(shuō)得再具體些?”問(wèn)號(hào)話一出口便自感悖論之嫌:抽象的能具體嗎?
冒號(hào)自明其意:“首先,抽象有角度之分。相同的實(shí)體(entity)經(jīng)過(guò)不同角度的抽象,得到的模型(model)也會(huì)不同。就拿人這個(gè)實(shí)體來(lái)說(shuō),在拓?fù)鋵W(xué)家眼里是三維連通集合,在理論力學(xué)家眼里是質(zhì)點(diǎn),在化學(xué)家眼里是碳水化合物——”
嘆號(hào)接嘴:“在情人眼里是西施。”
“過(guò)濾缺點(diǎn),抽取優(yōu)點(diǎn),西施就是這樣煉成的。”冒號(hào)故意拉長(zhǎng)了尾音。
眾人不禁一樂(lè)。
冒號(hào)繼續(xù)講解:“其次,抽象還有程度之別。抽象程度越高,細(xì)節(jié)越少,普適性越強(qiáng)。典型的例子如:從矩形到多邊形、從多邊形到一般形狀。”
引號(hào)問(wèn)道:“抽象對(duì)于軟件設(shè)計(jì)有何現(xiàn)實(shí)意義?”
冒號(hào)回答:“軟件設(shè)計(jì)者的任務(wù)是將復(fù)雜混沌的現(xiàn)實(shí)世界映射到精確嚴(yán)格的虛擬世界,要完成這種多對(duì)一的映射,抽象無(wú)疑是必由之路。在軟件需求分析階段,多通過(guò)屬性導(dǎo)向式抽象(property-oriented abstraction)用邏輯語(yǔ)言來(lái)描述系統(tǒng);在軟件設(shè)計(jì)階段,多通過(guò)模型導(dǎo)向式抽象(model-oriented abstraction)用模型語(yǔ)言來(lái)設(shè)計(jì)系統(tǒng);在編碼階段,常用兩種抽象機(jī)制:一種是參數(shù)抽象(abstraction by parameterization),一種是規(guī)范抽象(abstraction by specification)。”
句號(hào)望文生義:“參數(shù)抽象是不是指將函數(shù)代碼中的一些特殊值作為參數(shù)來(lái)傳遞?”
“不錯(cuò),這是最普通最常用的一種抽象方式。函數(shù)的每一個(gè)參數(shù)都是一種泛化,是對(duì)它所代表的所有可能值的一種抽象。我們看一個(gè)簡(jiǎn)單的例子。”冒號(hào)說(shuō)完在黑板上寫(xiě)下一段代碼——
int gcd(int a, int b)
{
while (a != b)
(a > b) ? (a -= b) : (b -= a);
return a;
}
冒號(hào)考問(wèn):“請(qǐng)問(wèn)這個(gè)函數(shù)是干什么的?”
眾人看了好一陣,有人猶豫地說(shuō),好像是求最大公約數(shù)吧?
冒號(hào)肯定道:“它確實(shí)是求最大公約數(shù)的,由兩個(gè)正整數(shù)輾轉(zhuǎn)相減而得。該函數(shù)通過(guò)參數(shù)抽象讓a和b分別代表任意正整數(shù),使之具有更強(qiáng)的普適性和重用性。問(wèn)題是除了該代碼的作者,其他人如何重用此函數(shù)?你不能假設(shè)用戶(hù)知道gcd是Greatest Common Divisior的縮寫(xiě),也不能假設(shè)他的數(shù)學(xué)程度,甚至不能確定他是否能看到源代碼。短短兩行代碼的函數(shù)尚且如此,何況更加復(fù)雜的函數(shù)?”
引號(hào)答道:“那只能靠文檔注釋了。”
“非常正確!”冒號(hào)頷首,“這是注釋文檔最重要的作用。沒(méi)有文檔的API如同沒(méi)有說(shuō)明書(shū)的產(chǎn)品,用戶(hù)是不敢輕易使用的。合格的文檔注釋中至少應(yīng)包括先驗(yàn)條件(precondition)和后驗(yàn)條件(postcondition),分別指代碼執(zhí)行前后必須滿足的條件。對(duì)于函數(shù)gcd而言,先驗(yàn)條件是:a、b均為正整數(shù),后驗(yàn)條件是:返回輸入二數(shù)的最大公約數(shù)。有了文檔注釋或規(guī)范說(shuō)明(specification)的函數(shù)成為使用者與實(shí)現(xiàn)者之間的一種契約——使用者只能依賴(lài)規(guī)范,實(shí)現(xiàn)者必須滿足規(guī)范。這種通過(guò)規(guī)范使代碼的功能與實(shí)現(xiàn)相分離的方法便稱(chēng)為規(guī)范抽象。其好處是顯而易見(jiàn)的:一方面,使用者不必閱讀代碼即可了解并使用它們;另一方面,實(shí)現(xiàn)者不必閱讀或改寫(xiě)其他代碼,只需在遵循規(guī)范的基礎(chǔ)上修改本地代碼,并且不用擔(dān)心影響客戶(hù)代碼。比如我們可以用輾轉(zhuǎn)相除法重新實(shí)現(xiàn)gcd。”
冒號(hào)隨手又寫(xiě)了幾行代碼——
int gcd(int a, int b)
{
return (b != 0) ? gcd(b, a % b) : a;
}
逗號(hào)未見(jiàn)其妙:“這兩種抽象機(jī)制在實(shí)際編程中經(jīng)常用到,只是以前不知道它們的學(xué)名罷了。”
冒號(hào)正色道:“它們看起來(lái)雖然很基本,但平淡之中見(jiàn)真功。如果一個(gè)程序員能合理設(shè)計(jì)參數(shù)、嚴(yán)格遵循規(guī)范、有效制定規(guī)范,便已是難得之才了。”
眾人捫心自問(wèn),離此要求確有相當(dāng)距離。
冒號(hào)續(xù)道:“借助參數(shù)抽象和規(guī)范抽象,我們可以實(shí)現(xiàn)過(guò)程抽象(procedural abstraction)和數(shù)據(jù)抽象(data abstraction)。其中過(guò)程抽象容易理解,任何一個(gè)函數(shù)都是過(guò)程抽象的結(jié)果,它賦予程序員定義新運(yùn)算或子程序的能力,是結(jié)構(gòu)化編程(Structured programming)和命令式編程(Imperative programming)的關(guān)鍵。下面我們重點(diǎn)談?wù)剶?shù)據(jù)抽象,它是對(duì)象式編程(OOP)的起源。”
posted on 2008-07-11 05:03 鄭暉 閱讀(2386) 評(píng)論(3) 編輯 收藏 所屬分類(lèi): 冒號(hào)和他的學(xué)生們