程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 冒號和他的學生們(連載22)——抽象思維

冒號和他的學生們(連載22)——抽象思維

編輯:關於JAVA

22.抽象思維

是謂無狀之狀,無物之象,是謂惚恍               ——《老子·道經》

冒號健步走進教室,學員們立刻正襟危坐,進入戰備狀態。

“如果說咱們是在合演一場戲,那麼前面五節課只是一個過門。”冒號俨然一副自導自演的架勢。

眾人暗暗吃驚:這過門也忒長了點吧。

冒號隨即探問:“還記得在范式總結中提到的迭代學習法吧?”

引號迅速應答:“就是在具體知識與抽象理論之間做折返跑。”

“記性不錯。”冒號贊道,“在上本班之前,你們已經具備了一定的編程語言的基礎,因此我們先從抽象的編程范式談起,此後又回歸到編程語言的討論。”

句號推測:“照此邏輯,下面我們將再次返回編程范式?”

“我們的確要來個For Loop,但相信這是一個增量式的循環。”冒號用編程語言來強調他的學習理論。

逗號有些失望:“按計劃不是該對Java語言作專題討論嗎?”

“你放心,Java它跑不了。”冒號看出他的心事,“語言是形,范式是神,這次我們將二者融合,爭取做到形神兼備。具體地說,范式以OOP為主,語言以Java為主,同時可能涉及C、C++或C#等語言。另外,示例代碼也會明顯增多。”

逗號臉上的一抹烏雲頓時消散開來。

冒號接著提醒道:“不過,秉承開班發言中的理念,我們的重心不在知識的枝節,而在知識的本源。因此無論討論Java還是OOP,我們不追求系統和全面,但力求從不同的選點、角度和深度來展示知識的活性。”

講到此處,冒號冷不丁提問:“程序員最重要的能力是什麼?”

眾人的答案五花八門:學習能力、邏輯思維能力、解決問題能力、專注力、溝通能力等等。

“毫無疑問,你們所說的都很重要。這本是個見仁見智的問題,我只是借此展開今後的話題。在我看來,抽象思維能力是最重要的。當然,不獨計算機領域,其他科學同樣需要這種能力。更廣泛地說,抽象是人類認識和描繪世界最首要的工具。”不知不覺冒號又走上了形而上的路線。

歎號這時想起:“前面談切面范式時,似乎專門提到過抽象與分解的重要性。”

“我們也曾提到,不同的范式正是對軟件進行了不同角度的抽象和分解。”冒號加以補充,“那麼什麼是抽象呢?不妨概括為:去粗取精以化繁為簡;由表及裡以異中求同。再精煉些,抽象就是作減法和除法。”

問號半信半疑:“減法好理解,通過甄選減去非本質和不重要的部分,即去粗取精。可除法呢?”

句號忽然悟道:“透過現象看本質,發現不同事物之間的相同之處,即異中求同。同類歸並,那就是除法了。”

冒號進一步解釋:“用離散數學或抽象代數的語言來說,通過抽象而產生等價關系以及相應的等價類,便是集合的商運算。”

逗號嘀咕:“本來快明白了,經這麼一描述,重新糊塗了。”

冒號笑道:“如果嫌數學語言高深,就用算術語言吧。乘法可看作同類復制,作為逆運算的除法自然是同類歸並了。”

逗號眼中的迷惘漸漸散去,若有所悟:“嗯,經過減法和除法,大數變小數,復雜變簡單。”

“能否把抽象說得再具體些?”問號話一出口便自感悖論之嫌:抽象的能具體嗎?

冒號自明其意:“首先,抽象有角度之分。相同的實體(entity)經過不同角度的抽象,得到的模型(model)也會不同。就拿人這個實體來說,在拓撲學家眼裡是三維連通集合,在理論力學家眼裡是質點,在化學家眼裡是碳水化合物——”

歎號接嘴:“在情人眼裡是西施。”

“過濾缺點,抽取優點,西施就是這樣煉成的。”冒號故意拉長了尾音。

眾人不禁一樂。

冒號繼續講解:“其次,抽象還有程度之別。抽象程度越高,細節越少,普適性越強。典型的例子如:從矩形到多邊形、從多邊形到一般形狀。”

引號問道:“抽象對於軟件設計有何現實意義?”

冒號回答:“軟件設計者的任務是將復雜混沌的現實世界映射到精確嚴格的虛擬世界,要完成這種多對一的映射,抽象無疑是必由之路。在軟件需求分析階段,多通過屬性導向式抽象(property-oriented abstraction)用邏輯語言來描述系統;在軟件設計階段,多通過模型導向式抽象(model-oriented abstraction)用模型語言來設計系統;在編碼階段,常用兩種抽象機制:一種是參數抽象(abstraction by parameterization),一種是規范抽象(abstraction by specification)。”

句號望文生義:“參數抽象是不是指將函數代碼中的一些特殊值作為參數來傳遞?”

“不錯,這是最普通最常用的一種抽象方式。函數的每一個參數都是一種泛化,是對它所代表的所有可能值的一種抽象。我們看一個簡單的例子。”冒號說完在黑板上寫下一段代碼——

int gcd(int a, int b)
{
   while (a != b)
     (a > b) ? (a -= b) : (b -= a);
   return a;
}

冒號考問:“請問這個函數是干什麼的?”

眾人看了好一陣,有人猶豫地說,好像是求最大公約數吧?

冒號肯定道:“它確實是求最大公約數的,由兩個正整數輾轉相減而得。該函數通過參數抽象讓a和b分別代表任意正整數,使之具有更強的普適性和重用性。問題是除了該代碼的作者,其他人如何重用此函數?你不能假設用戶知道gcd是Greatest Common Divisior的縮寫,也不能假設他的數學程度,甚至不能確定他是否能看到源代碼。短短兩行代碼的函數尚且如此,何況更加復雜的函數?”

引號答道:“那只能靠文檔注釋了。”

“非常正確!”冒號颔首,“這是注釋文檔最重要的作用。沒有文檔的API如同沒有說明書的產品,用戶是不敢輕易使用的。合格的文檔注釋中至少應包括先驗條件(precondition)和後驗條件(postcondition),分別指代碼執行前後必須滿足的條件。對於函數gcd而言,先驗條件是:a、b均為正整數,後驗條件是:返回輸入二數的最大公約數。有了文檔注釋或規范說明(specification)的函數成為使用者與實現者之間的一種契約——使用者只能依賴規范,實現者必須滿足規范。這種通過規范使代碼的功能與實現相分離的方法便稱為規范抽象。其好處是顯而易見的:一方面,使用者不必閱讀代碼即可了解並使用它們;另一方面,實現者不必閱讀或改寫其他代碼,只需在遵循規范的基礎上修改本地代碼,並且不用擔心影響客戶代碼。比如我們可以用輾轉相除法重新實現gcd。”

冒號隨手又寫了幾行代碼——

int gcd(int a, int b)
{
   return (b != 0) ? gcd(b, a % b) : a;
}

逗號未見其妙:“這兩種抽象機制在實際編程中經常用到,只是以前不知道它們的學名罷了。”

冒號正色道:“它們看起來雖然很基本,但平淡之中見真功。如果一個程序員能合理設計參數、嚴格遵循規范、有效制定規范,便已是難得之才了。”

眾人扪心自問,離此要求確有相當距離。

冒號續道:“借助參數抽象和規范抽象,我們可以實現過程抽象(procedural abstraction)和數據抽象(data abstraction)。其中過程抽象容易理解,任何一個函數都是過程抽象的結果,它賦予程序員定義新運算或子程序的能力,是結構化編程(Structured programming)和命令式編程(Imperative programming)的關鍵。下面我們重點談談數據抽象,它是對象式編程(OOP)的起源。”

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved