前言以往我們講抽象化首先會在物件導向提及,初學物件導向的人常常被抽象化這個概念跟名詞搞糊塗了,但是其實我們都知道抽象化是什麼,只是我們常常使用反而覺得習以為常,真正去細讀程式語言的發展史,抽象化其實不專屬於物件導向,每個階段的演進或多或少都是在做抽象化的展現,只是層次高低不同罷了。
觀念介紹
在討論之前我們先來定義什麼是抽象化吧,抽象化就是把詳細的實作細節隱藏起來,用一個代表實作細節概念的詞或真實世界複雜環境一部份事物的特徵來包裝或代替我們真實的系統。
拿筆者的巴哈佈景圖舉例吧......
抽象化之前
抽象化過後
會拿畫作來舉例是因為,我們人類初步理解事物的方式也是抽象化的方式,有的人你甚至只要告訴他哪邊要配哪種顏色,它就會自動幫你畫上明暗.漸層.陰影。
不說別人,說你自己,你在畫一幅畫的時後,肯定腦海中有個無法清楚界定細節的色塊資訊,意思是說我只要大略知道某些色塊用了哪種顏色,我就可以快速拼湊這些色塊資訊,當我色塊分的越大(意旨抽象化層次),也代表著我拼湊色塊的速度也提升了。
如果在作畫前連色塊大致分佈都不清楚,就要開始想說哪邊要用哪種漸層,一步一步慢慢來,這樣就太累也太慢了,而且若腦中沒有大致的色塊分佈,結果很容易不如預期。
我們的程式語言也不斷幫人類做類似的色塊分類動作,基本上從組合語言開始,我們就試圖要讓電腦語言能夠更抽象,幫助人類解決編程實作的困難。
抽象化所代表的意義是程式的生產力提升,而這其中的差別便是抽象化的層次高低,層次高的抽象化理所當然可以增加我們程式設計師的產能,除了生產力,可維護性的也是越來越講究,而越高層次的抽象化,越能應付需求變動。
以剛才那幅畫為例,假設我們是用小畫家處理,我必需把畫面的漸層跟光影都弄的很細很細,直接把實作細節呈現在同一張圖,那麼當需求改變:比方說我要你改變陰影的位置,配色的色感,那麼這幅畫基本上它會變得非常難調整,因為它把所有細節都呈現在同一個程式裡(可以想成所有細節集中在單一圖層實作)造成我沒有辦法輕易進行處理。
程式語言的抽象化層次
俗語說,工欲善其事,必先利其器,想讓電腦運行我們寫好的系統,必需要有程式語言這個工具,事實上我喜歡把程式語言比作兵器,越高層次的抽象化越能輕易達到目的。
比方說我現在要殺掉一萬個人......
這裡有幾樣工具給你選擇,分別是你的金手指.美工刀.長劍.陶瓷子彈的火槍.手槍.......
機械語言
組合語言
高階語言-命令式程式設計
高階語言-程序式程式設計
物件導向程式設計
你可以發現我用兵器差異來分別抽象化層次,各位不妨先建立這個既定印象,以便接下來一一作詳細的理清。
簡單來說最初機械語言是最接近電腦實作細節的語言,可是到處都是 0 跟 1 太難理解了,於是我們希望讓機械語言更容易理解,所以才用組合語言利用組譯器把某部份機械語言的細節包裝起來。
這個時後我們發現,組譯器這個翻譯員雖然稱職,但是它不知變通,只會一一對應一一翻譯,換了機器就傻眼,而且程式設計師仍然必需了解實作細節,它只不過是幫這些細節做了對應的美容,比如原本機械語言指令的部份是用 0 跟 1 來描述,組合語言把它表達成某個指令的英文縮寫,又如機械語言中對記憶體位址的表示也是 0 跟 1 ,組合語言也只是把它用方便查閱的 16 進位來表示。
簡單來說,組合語言包裝的細節是 0101 資料本身的意義,也就是說你基本上還是必需知道和機械語言幾乎接近的操作細節,有夠笨的對吧?但它確確實實改善了機械語言這樣的無字天書,如果只有 0101 ,我們玩不下去的。
接下來一下子從美工刀跳到長劍,為什麼組合語言和高階語言之間有這樣一個超大的鴻溝呢?
因為相對於組合語言,高階語言有決定性的抽象化革命,這個階段,我們在乎的是撰寫難度,並不在乎程式內容的抽象化,筆者喜歡把它稱作第一階段抽象化,第一階段抽象化的著眼點在於,如何以接近人類所能理解的語法代替現有的指令式表示法(機械語言.組合語言)。
簡言之第一階段抽象化是以人類能理解的語法形式將程式語言的實作細節抽象化,讓程式語言更接近人類,習慣上我們把這類語言稱作高階語言,事實上我們現在所使用的高階語言,都是藉由語法的抽象化這一個關鍵演進關卡產生的。
語法抽象化就是把低階語言中人類不能直覺了解的機器指令,包裝在程式語法中,隱藏其複雜的運行過程,而這一個階段之所以可以叫作革命性演進,是因為它不只把組合語言的易讀性提高,它也做到低階指令操作細節的包裝,而這其中只要短短一行陳述就能處理好幾個機械指令呢。
對於語法的抽象化,編譯器可比組譯器來得聰明的多了,因為它能理解更抽象的高階語言,還能因地制宜的對一些命令進行最佳化。
這一個階段有一個非常有名的產物,也就是強大的C語言,到目前為止,C語言仍舊運用於:系統程式的開發.即時系統及高效能系統的開發,而且C語言有著簡單的語法.高可攜性.程序導向的特性,而這種種特點也讓C語言依舊風采未盡。
在來我們又發現第二個鴻溝,那就是命令式語言跟程序式語言,雖然高階語言現在基本上同時是命令式也是程序式,但是在早期還是有程式語言沒有程序導向的概念導入,純脆命令式的高階語言到程序導向基本上又是一個革命性的演進。
這個演進雖然不求語法的革命性改變,但卻開始有了程序的概念,也就是將好幾個命令包裝成各種不同的子程序,這樣一個概念在跟本上也讓系統開發的速度加快了許多,像C語言本身內建的.h檔,就是一個子程序庫,裡面包含了很多子程序。
這個包裝也是一種抽象化喔,記得,抽象化從來就不是物件導向的專利,抽象化可以指很多事物的抽象化,例如子程序就是概念的抽象化,把概念相同或表達特定演算法的程式碼片段包裝起來,不只可以達成程式碼的重用,也讓大型軟體的拼裝變得更不費力,因為我可以透過一個又一個不同功能的子程序來完成一個大程式。
int main()
{
...
if(abs(distance) == 0)
{
maxdistance = analyze(maxdistance);
}
...
}
int abs(...)
{
abs子程序程式片段
}
int analyze(...)
{
analyze子程序程式片段
}
到了物件導向這個階段,單純的包裝概念已經無法滿足我們對大型系統的需求了,軟體系統的功能越來越複雜,系統越來越講求互動性,再加上客互也越來越講求快速應變,因而開發時程不停縮短,系統開發的不確定因素和挑戰隨著時代的演進越演越烈。
到了物件導向原則上又是一個革命性的轉變,這個時後物件導向已經不只強調概念的包裝了,物件導向更強調讓思維引導我們包裝程式跟組建系統,也就是說物件導向基本上是一種思維,它已經不是單純的某一種程式語言了,而是思維的大改變。
這也是為什麼我前面一直講,思維不轉變就不會懂得物件導向的好,因為物件導向從來就不是單純一種程式語言,它的概念是必需依附在程式語言本身的,也就是說你如果不懂得物件導向的思維,你所寫出來的程式最多也就是程序式的物件導向程式語言,就像學國語,如果只懂得發音的方式而不懂得培養語感,最終說出口的也不過是令人匪夷所思的臺灣國語。
首先一個思維的大轉變,物件導向強調以實體跟資料為核心組織邏輯,並以物件為基本的模組單位,也就是說,在物件導向裡,子程序就是物件,而物件是具體事物和抽象概念的模擬,可以讓程式設計更接近人類的認知模式,和程序導向解決問題的方式不同,舉例:
程序導向是-吃(人,炒麵)
物件導向是-人.吃(炒麵)
可以看出兩者的不同嗎?程序導向是以功能組件來開發程式,物件導向是以物件組件開發程式,從資料的角度跟系統的結構來看,物件導向建立的模組,其內部的子程序通常容易符合內聚力的原則,在某些情況下,程序導向會產生一些物件導向所沒有的問題,如果我有一群子程序是要應付相同的資料對象?我該如何是好?
畢竟子程序不像物件有成員變數,可以更改或儲存其內部的狀態,子程序往往分散在系統不同的角落,然而這些子程序所處理的資料對象又是相同資料對象,這往往會讓系統內部出現很多重複的程式碼,也讓系統產生過多不當耦合,而物件導向要做的,就是把這些對應真實實體資料的函數邏輯全綁在一起,減少過多不當耦合跟重複程式碼的問題。
總結
綜觀程式語言發展史,程式語言的發展總是不斷朝著更高層次抽象化的目標邁進,儘管我們還有更多努力空間。
接下來的章節筆者將詳細介紹物件導向各種實作概念的美妙之處。