抽象化資料型別(Abstract Data Type)」是物件導向設計的重要核心觀念之一。能否妥善地應用抽象化方法於設計中,更是設計成敗的重要關鍵。就我個人的觀察,為數不少的設計者,即使以物件導向程式語言來撰寫程式,卻鮮少運用抽象化的技巧,這無疑是喪失了物件導向設計所能帶來的極大好處。
抽象化方法:萃取重要的特性,摒除不重要的細節
不論是資料結構或物件導向程式設計的課程,都會提到「抽象化資料型別」的觀念,但對部分設計者來說,要將這虛無縹渺的設計觀念,對應到具體的設計程序中,是有障礙的。
我建議應該從最源頭開始理解。什麼是「抽象」呢?它源自於拉丁文中的 「Abstracio」,意指抽取、排除。而「抽象化方法」是人類認識事物的方法。在這種方法下,我們思考事物的特性時,會將焦點放在我們關心的特性上,而暫時忽略其他特性。也就是說,將不那麼關心的特性予以抽離、排除,進而可以更純粹的方式,探討、思考關心的特性,進而更了解事物的本質。
這樣子的理解方式,同樣適用於觀念的表達。由於希望接收者能以最有效率的方式,明白我們所要表達的核心精神,那麼我們也勢必要摒除重點之外的特性,僅萃取最核心的特性。倘若鉅細靡遺地展現出所有細節,接收者勢必難以捕捉到核心精神。
重要特性代表的是通則,也因此才有重複使用的機會
那麼,什麼樣的特性是重要的呢?
基本上,適用範圍越廣泛的,就是越重要的特性,換句話說,是較為一般(General),而不特殊(Special)。特性越一般的,所涵蓋的情況和例子也越多,而越特殊的則相反。這樣子的特性,代表的是「通則」而非「特例」;在概念上較為「高階」;所呈現的是「綱要」而非「細節」;它偏向觀念層次,較不接近實作層次;通常這樣的特性也較不易發生變動。而且,我們會說這樣的特性,是比較抽象的特性。
對物件導向設計來說,具備較抽象特性的類別,就是較為抽象的類別,反之,則為比較具象的類別。在物件導向設計所提供的繼承機制中,父類別就是較為抽象的類別,而子類別便是較具象的類別。
在一層又一層的繼承體系中,類別的抽象程度亦有不同層級的差異。
在設計上,我們會傾向於努力讓設計更能被重複運用、更不容易受到改變所影響、更容易被理解。而這幾項特性,正好與抽象化的結果相符。經過抽象化所得到的類別,因為所代表的是「通則」,有更廣泛的適用範圍,自然有更多重複運用的機會。
即使所面對的對象發生了變動,也會因為適用的範圍夠廣泛,而不致於受影響。同樣的,因為這樣的類別表達出比較高階的觀念,那麼類別本身及特性,自然更容易理解。
經驗不多的設計者,可以在實作之後再抽象化
在設計軟體時,有經驗的人,對於需求的未來走向,或現有需求的既定脈絡掌握度較高,能在設計之初立即意識到「某些實作之間,不過只是同一個通形下的變形」,甚至預測到未來可能會有的各種變形,因而做出適當的抽象化安排。而厲害的設計者,即使沒有實際遭遇到各種實體變形,也能一刀畫出抽象與具象的分界點。
但對設計經驗不是很豐富的設計者來說,或許在設計的最初,完全不執行抽象化,等到日後發現可抽象化的特性時,仍然能夠透過類別演化的方式來抽象化。
在這種情況下,設計者受限於經驗,識別不出任何可抽象化的可能性,所以便直接寫下最具象、最低階的實作類別。
但是,當遭遇到擴充或者是修改的需求時,便會發現「為了滿足需求,得發展出另一個實作類別」,或許他會發現,這個實作類別,與既有的實作類別有著某種程度的共通性,所以他會將新舊實作類別共通的部分提煉出來,形成二者的父類別,並在實作類別中,保有彼此的相異之處。
例如,我們一開始要為系統設計一個使用者認證機制,最初的需求是要從關聯式資料庫中的帳號表格裡,讀出使用者的ID及密碼,並且與使用者輸入的ID及密碼相比較。所以設計出一個Authenticator類別來處理。
我們不能預測未來可能會有的認證機制實作方式,所以把這個類別直接擺放在最低階的實作層次上。有一天,新需求被提出來,系統得增加LDAP的認證方式。你可以選擇直接加上一個新的LDAPAuthenticator,甚至是直接修改舊有的Authenticator類別(如果不再需要的話)。採取這種方式,你是不會特別去執行任何的抽象化,新類別依舊放在最低階的實作層次上。
但是,你也可以選擇抽象化,找出能夠對舊類別和新類別同時成立的特性──也就是「認證一組ID、密碼是否正確」。這樣的特性所涉及的實作成分較少,因而提煉出來放在父類別(維持舊有的Authenticator名稱),那麼便可據之衍生出RDBMSAuthenticator及LDAPAuthenticator兩個子類別,並且將實作的細節放至這兩個類別中。
這麼一來,高階與低階、抽象與具象、一般與特殊、通則與特例、觀念與實作、不易變與易變的特性,就分別拆解至父類別及兩個子類別中。
抽象化會萃取出抽象化介面及一般化規則
一般來說,抽象化動作所萃取出來的,大致上可分為抽象介面及一般化規則。
抽象的介面便是通形所具備的介面。像在Authenticator的例子中,經由抽象化,找出了各Authenticator子類別應具備的函式(例如boolean authenticate(String id, String password)),但這函式在Authenticator父類別中並無具體的定義,它只是個抽象的函式(abstract method),具體的定義是在子類別中完成的。此種抽象化的產物是介面,也就是所有具象類別共同具備的形貌,以及它們皆應遵守的規格。
而所謂一般化的規則,便是所有變形都具備的演算邏輯。一般化的規則不同於虛擬的函式介面,它具有實際的程式碼。但即使如此,也不涉及低階的實作細節。假設,我們針對不同類型的快速排序演算法,都各自設計一個類別,對於整數有IntQuickSort;對於浮點數有FloatQuickSort;對於字串有StringQuickSort。
這三個類別,有共通的一般化規則,也就是排序演算法本身。也就是說,單看演算法本身,這三者並沒有什麼分別,只有比較大小的方式有所不同罷了。所以我們可以抽象化,萃取出一個名為QuickSort的類別,裡頭有個叫做sort()的函式,並在這個函式上,寫下快速排序演算法,而將如何比較大小的部分,置於各子類別中實現。
一般化的規則有時不涉及實作的細節,所以它單純地只是各子類別中重複的邏輯。但有時,它會涉及實作的細節,但細節當然不會在這一般化的規則中定義,而是在子類別中完成,所以,這樣的一般化抽象規則,必須搭配著抽象介面。
值得一提的是,當變形的數量可能很多時,這種抽象化的方式,可能會帶來類別數量的爆炸。而有名的策略(Strategy)設計模式,便將一般化規則萃取,成為一個和各變形類別間沒有繼承關係的類別,單純讓各變形類別的父類別,扮演各變形類別的抽象介面。
作者簡介:
王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。
熱門新聞
2025-01-13
2025-01-10
2025-01-13
2025-01-10
2025-01-10
2025-01-10