面向對象的哲學體系及科學體系的探討

目錄 Catelog

序言 Perface

真經第一章:世界 Waltanschauung

真經第二章:抽象 Abstraction

真經第三章:層次 Arrangement

真經第四章:繼承 Inheritance

真經第五章:耦合 Couple

真經第六章:運作 Moving

真經第七章:建造 Build

真經第八章:刻畫 Delineate

真經第九章:模式 Pattern

真經第十章:悟道 Doctrine

後記 Afterword

參考文獻 Reference

序言 Preface

"佛曰:苦海無涯,回頭是岸。——佛教用語"

面向對象(Object-Oriented),這是一條令無數開發人員魂牽夢繞的短語。幾乎每個軟件分析師、設計師和程序員都時刻將它銘記於心,對它頂禮膜拜。然而,對大多數人來說,它又像是天邊的霞光,可望而不可及,無數次伸出雙手,總是抓不住這虛無縹緲的聖物。於是,我們依然每天將面向對象高高供其,卻始終無法悟得其道,更不要談嫻熟運用其道法了。

面向對象像一灘苦海,無數人遊弋其中,卻久久不得其要領;類、對象、繼承、多態、接口、UML、設計模式……無數概念看得我們眼花繚亂,卻也悟不透其真諦。佛教有云:苦海無涯,回頭是岸。如果置身苦海中無法脫離,那麼,我們是否應該提高一個層面去看這片苦海:從哲學及科學的角度,去審視面向對象。

曾有人說:藝術的極致是科學,科學的極致是哲學。此話不無道理,牛頓、愛因斯坦等科學界泰斗,在其後期都不約而同地轉向哲學研究。當然,這裡本人無意更不敢將自己與上面兩位大師相提並論,而且本人也不奢求此文能成為一篇頗有思想的佳作。只不過,本人在平時的實踐和思考中,略有小得,於是,在這裡拿出,和大家一起分享討論。雖然膚淺,但希望本文能成為一絲波紋,為各位脫離苦海提供一點點的推動作用。

真經第一章——世界 Weltanschauung

"世界觀(德文:Weltanschauung)意為'著眼世界之上',是人們對世界的總的根本的看法。任何哲學問題的探討,歸其出發點和本源,都是世界觀的問題。什麼樣的世界觀決定了什麼樣的哲學

觀點。——馬克思"

1.1、看世界

我們知道,哲學領域中,最根本的對立是唯物主義和唯心主義的對立,而附屬其下,又有許多對立,如形而上學和辯證法的對立、可知論和不可知論的對立等等。這些對立形成了哲學的基本體系、派別和出發點。實際上,這些對立,都是世界觀的對立。世界觀,簡而言之即如何看待這個世界。世界觀是一切哲學問題的本源和出發點。

同樣,在程序世界裡,也有著不同的世界觀。而這其中最根本的對立便是過程論和對象論的對立,這個對立,衍生出了面向過程和麵向對象兩種方法論。於是,要真正理解面向過程和麵相對象,我們就不得不先深究一下程序世界中這兩種世界觀。

首先要提到的是,不論是過程論還是對象論,都承認一點,那就是程序世界本質上只有兩種東西——數據和邏輯。數據天性喜靜,構成了程序世界的本體和狀態;邏輯天性好動,作用於數據,推動程序世界的演進和發展。

儘管上述觀點是統一的,但是在數據和邏輯的存在形式和演進形式上,過程論和對象論的觀點截然不同。

過程論認為:數據和邏輯是分離的、獨立的,各自形成程序世界的一個方面(Aspect)。所謂世界的演變,是在邏輯作用下,數據做改變的一個過程。這種過程有明確的開始、結束、輸入、輸出,每個步驟有著嚴格的因果關係。過程是相對穩定的、明確的和預定義的,小過程組合成大過程,大過程還可以組合成更大的過程。所以,程序世界本質是過程,數據作為過程處理對象,邏輯作為過程的形式定義,世界就是各個過程不斷進行的總體。

對象論認為:數據和邏輯不是分離的,而是相互依存的。相關的數據和邏輯形成個體,這些個體叫做對象(Object),世界就是由一個個對象組成的。對象具有相對獨立性,對外提供一定的服務。所謂世界的演進,是在某個"初始作用力"作用下,對象間通過相互調用而完成的交互;在沒有初始作用力下,對象保持靜止。這些交互並不是完全預定義的,不一定有嚴格的因果關係,對象間交互是"偶然的",對象間聯繫是"暫時的"。世界就是由各色對象組成,然後在初始作用力下,對象間的交互完成了世界的演進。

1.2、一道智力題引發的思考

上面的描述也許有些不夠直觀,那麼,下面我們通過一個實際的例子,直觀感受一下在兩種世界觀下,對同一件事物是怎麼看的。

大家都聽過這麼個智力題吧:

說有甲、乙、丙三人住店,一間房30。於是每人10元,共計給店老闆30元住進一間房。後來店老闆發現弄錯了,房價應該是25元,於是給小二5元讓小二退給房客。小二黑心,貪汙了2元,退給甲乙丙每人1元。這樣房客每人付了10-1=9元,三九27,加上小二貪汙的2元,共29元,問那1元哪裡去了?

不知各位聰明的看官是否已經參透其中玄機。不過參不透也沒有關係,這不是重點,重點是,我們現在來分別用過程論和對象論分析一下這件事。

首先,我們來看看過程論是怎麼看這件事情的。


面向對象的哲學體系及科學體系的探討


圖1.1、過程論看世界

如圖1.1所示,這就是過程論下看這件事的樣子。左邊是過程的各個步驟,而右邊紅字表示在每個過程步驟的數據情況,這種數據情況反映了世界當前的狀態。為簡單起見,我們只考慮在這個過程中參與分配的數據。

初始時甲乙丙各10元,老闆和小二沒有錢,這可以認為是這個過程的初始狀態,這些數據是輸入。隨著各個步驟的進行,數據不斷更新,而在每個步驟,數據如何更新、更新多少,都是由步驟嚴格確定的。經歷五個步驟後,數據變為甲乙丙各1元,老闆25元,小二2元,這就是終止狀態,也是這個過程的輸出。

下面,再來看看對象論下如何看這件事。

面向對象的哲學體系及科學體系的探討

圖1.2、對象論看世界

對象論眼中,世界是由各種對象組成的,每個對象有自己的數據和邏輯,如圖1.2所示。在這件事裡,有五個基本對象:甲、乙、丙、小二和老闆(注意,這裡我們還沒有提到類和抽象等概念,所以不要讓固有思維跳出來,在這裡要只認識對象,不認識類等概念。現在我們只討論世界觀的基本問題:程序世界的本質,至於更具體的問題,留待後面討論)。每個對象有自己的一系列數據和邏輯,這裡只列出了我們關心的部分。

然後呢?沒有然後了。沒錯,在對象論眼裡,這就是這件事的本質模樣,這件事所涉及的東西就是這麼幾個對象,本來它們各自獨立,老死不相往來。只不過在"住店"這個外部驅動力下,幾個對象"偶然"、"暫時"互相聯繫,利用其他對象提供的公開服務,完成了一些交互。在交互中,各自的數據可能會發生一些變化,但對象的本質沒有變。這裡也要注意,這種交互雖然在一定程度上由既定邏輯預定義,但不像過程論認為"萬事萬物都已註定",在對象論下,對象間的交互是"偶然的"、"暫時的",這次五個人因為住店這個外部驅動力交互了一次。但下次如果魏國和蜀國交戰變為驅動力,他們間的交互就不是拿錢給錢了,而是刀兵相見。所以,對象論不認為"一切都已註定"。

1.3、總結

通過上面一個例子,不知各位是否已經明白程序世界中兩種世界觀看事物的不同。下面,有一些問題還要明確一下。

I. 過程論和對象論是兩種看世界的觀點,沒有孰對孰錯、孰好孰壞之分。

II. 過程論和對象論不是一種你死我活的絕對對立,而是一種辯證統一的對立,兩者相互滲透、在一定情況下可以相互轉化,是一種"你中有我、我中有你"的對立。如果將對象論中的所有交互提取出來而撇開對象,就變成了過程論,而如果對過程論中的數據和邏輯分類封裝並建立交互關係,就變成了對象論。

III. 過程論相對確定,有利於明晰演進的方向,但當事物過於龐大繁雜,將很難理清思路。因為過程繁多、過程中又有子過程,容易將整個世界看成一個紛繁交錯的過程網,讓人無法看清。

IV. 對象論相對不確定,但是因為以對象為基本元素,即使很龐大的事物,也可以很好地分離關注,在研究一個對象的交互時,只需要關係與其相關的少數幾個對象,不用總是關注整個流程和世界。但是,對象論也有困難。例如,如何劃分對象才合理?對於同一個驅動力,為什麼不同情況下參與對象和交互流程不一樣?如何確定?其實,這些困難也正是面向對象技術中的困難。

綜上,我們知道在程序世界中,存在著過程論和對象論兩種對立的世界觀,並且其各有千秋,無法定奪孰好孰壞。但是,對象論似乎更有助於分析規模較大的事物。本文是探討面向對象的,所以,在下文中,都會選擇對象論作為世界觀。這種以對象為本的世界觀,也是本文後續一切的基礎和出發點。

真經第二章——抽象 Abstraction

"金、木、水、火、土元素,構成宇宙萬物,並作為各種自然現象變化之基礎——五行說"

2.1、導言

上文探討了世界觀問題。我們知道,要想真正理解面向對象,首先要用對象論去審視世界。而在對象論中,萬事萬物的本源是對象,對象是組成世界的基本元素。但是,要真正看透一個世界,只有基本元素是不行的。

中國古代的樸素唯物主義哲學中,比較有代表性的是五行說。五行說認為,世界的基本元素是"金、木、水、火、土",但若說世界只有"金、木、水、火、土",也是不成的,所以後續有云:五行相生相剋,相互交織結合,組成了大千世界。雖然從現代科學角度看,五行說並不完全準確,但其有一點事非常正確的,那就是世界首先有基本元素,然後基本元素還要衍生出各種其它東西。

在第一章中,我們說了在對象論中,對象是組成世界的基本元素,但這還不能構成真正的世界。下面,我們來看看對象是如何構成和衍生出其它事物的。

2.2、類是怎麼來的

和真實世界中構成和衍生方式不同,程序世界中,最重要的衍生方式是抽象。例如,眾所周知的類(Class),就是從對象上首先抽象出來的概念。下面我們看一看類是怎麼來的。

從哲學角度說,先有對象,然後才有類,類和對象是"一般和特殊"這一哲學原理在程序世界中的具體體現。這可能和很多人的直覺不同,因為在具體寫程序時,是先定義類,然後才能實例化對象。在這裡,我們是從哲學層面進行探討,所以,對象是本源,類的概念是衍生。為什麼?因為從認識論來說,首先有具體認知能力,才能有抽象認知能力,抽象認知能力是一種高層的,人類特有的認知能力,它使我們可以從大量具體認知中,捨棄個別的、非本質的屬性,提取出共同的、本質的屬性,是形成概念的必要手段。

還是以住店的故事為例吧。在我們的世界觀中,那個故事涉及了五個對象,剛開始我們沒有抽象的概念,而只是從具體認知角度對這五個對象進行認知:首先是甲,他有頭、有身子、有胳膊有腿,頭上有眼睛鼻子耳朵,他還有個名字叫劉備,有個身份是顧客……除了這些數據,這個對象還可以做一些事情,可以吃飯、呼吸、喝水,還能給錢和拿錢……好的,一通認知後,我們對甲這個對象有具體認知了;然後,我們對乙進行認知:他有頭、有身子、有胳膊有腿,頭上有眼睛鼻子耳朵,他還有個名字叫關羽,有個身份是顧客……除了這些數據,這個對象還可以做一些事情,可以吃飯、呼吸、喝水,還能給錢和拿錢……認知完了,接著是丙、小二和老闆……當具體認知足夠多後,我們發現一件事情:這幾個對象很相似啊,有相似的數據(但具體值可能不同),有相同的邏輯,於是,我們的抽象認知能力告訴我們,這五個對象很相似,可以看做一類東西,於是,我們給出一個類,叫"人",並且認為這五個對象都是"人"這個類的具體例子,我們叫其為實例。以後遇到類似的對象,我們都可以知道,這個對象屬於"人"類。

面向對象的哲學體系及科學體系的探討

圖2.1、"人"類的由來

所以,類其實是抽象認知能力作用於程序世界的基本元素——對象後所衍生出來的抽象概念,是抽象思維在程序世界中物化後的產物。當然,現實世界中每個對象都有無數的數據和邏輯,但在具體到程序世界時,我們往往只關心具體場景中相關的數據和邏輯。例如,在住店場景中我們關心現金這則數據,至於這個人力氣大不大無所謂;而如果上戰場打仗,我們就關心攻擊力和力量,現金就不重要了。

2.3、為什麼要有類

知道了類是怎麼來的,那麼類的作用是什麼,我們為什麼需要類呢?

類可以幫助我們方便地認識和定義世界中的對象。這個作用是顯而易見的。例如當今世界有60幾億人,如果不會抽象思維,我們每遇到一個人,都要認知一遍:啊!這個對象有眼睛,有耳朵,有鼻子有嘴,有胳膊有腿……要是真這樣,世界也太瘋狂了。有了類的概念,我們就可以只記類的數據和邏輯,而對於具體對象,只要知道它屬於什麼"類",一切就都知道了,所需要區分的只是不同對象的數據具有不同值而已。

其實,這不僅僅是類的作用,我們進行抽象思維,就是為了這個目的。

2.4、總結

這一章敘述了類的哲學本質、衍生過程和作用。要記住,抽象是形成和衍生概念的基本方法,不只是類,後面的很多概念,都是通過抽象形成的。所以,我們可以說:上天只給了這個世界各種對象,但我們用抽象去更好地認識世界。

真經第三章——層次 Arrangement

"道生一,一生二,二生三,三生萬物——老子"

3.1、導言

上文提到,在對象論中,抽象是衍生概念的基本方法。但是你有沒有一個疑問?所謂抽象,是對許多對象撇開個性,抽出共性,這樣,抽象過程就不是確定的、唯一的。例如,我們在看過很多對象後,發現有一類對象有四個輪子、有發動機、可以駕駛、是可以被意識反映的客觀實在。我們抽象出一個叫"汽車"的類。這次抽象中,我們將有四個輪子看做了共性,但是,如果撇開這條性質,僅看後三條,摩托車、輪船、飛機都符合,於是,我們又可以抽象出"機動交通工具"類。再把有發動機撇掉,自行車、腳踏三輪車,甚至馬都符合,所以,又得出個"代步工具"類,最後,把可以駕駛也撇掉,只剩下"是可以被意識反映的客觀實在",如果這樣,所有物質都符合,這樣,就得出一個"物質"類。

這下子困難就來了,你說我家的奔馳應該歸到哪一類呢?我家的奔馳和一隻是不是一類東西呢?如果從前三類看,當然不是,但是從最後一個"物質"類看,又確實是一類東西。那到底哪一個對?事情究竟是怎樣的?其實答案很簡單:歸到哪一類都正確。至於後一個問題,無法回答,因為這個問題單獨問根本沒有意義。為什麼?

關鍵在於:抽象是有層次的。

3.2、世界是一棵樹

上文說到,對象是基本,我們從對象上抽象出類。但是,世界可並不是一層對象一層類那麼簡單,對象抽象出類,在類的基礎上可以再進行抽象,抽象出更高層次的類。所以經過抽象的對象論世界,形成了一個樹狀結構。

面向對象的哲學體系及科學體系的探討

圖3.1、抽象層次樹示例

圖3.1展示了一棵抽象層次樹的示例。不要懷疑,在對象論中,經過初步抽象思維加工後的世界就是這樣樣子。本來,世界只有各個具體對象(最底下紫色文字表示的層次),這是第0層,是一切抽象的本源和起始,然後,抽象思維作用其上,抽象出初步的類,然後在既有類和對象的基礎上可以再進行抽象……如此歸納下去,最終整個世界歸結於樹的根節點:本體。所謂本體,即萬物之源、萬物之本,是哲學層面上最高層次的抽象。在這裡,我們將其看成是一個特殊的類,作為抽象層次樹的根。

千萬不要小看了這棵抽象層次樹,如果能參透其中的奧秘,就能明白很多面向對象中的玄機,而且很多問題就都迎刃而解了。這種抽象層次樹理論也是後續諸多內容的理論基礎。例如,OO中重要的概念——繼承(Inheritance)和多態(Polymiorphism),如若探究其哲學本源,就是從這裡來的。

下面,對這棵樹做一些必要的說明。

I. 這是一棵單根樹,最頂層"本體"為唯一的根,最下層葉子節點為基本對象。一切中間節點都為類。

II. 越往上的類抽象層次越高,具體度越低,其內涵越小,外延越大;越往下的類抽象層次越低,具體度越高,其內涵越大,外延越小。說明一下,所謂類的內涵,是指類對屬於自己的對象的說明力度,而外延是指類能包含的具體對象的總和。例如,家用電器這個類,其內涵是使用電作為能源並完成特定功能的家用器具,各個電冰箱、洗衣機、電磁爐、遊戲機、DVD機等都在其外延之內;而娛樂家用電器這個類,作為比家用電器更低層次的類,其內涵除了"使用電作為能源並完成特定功能的家用器具"外,還要是具有娛樂功能,其內涵明顯大了,但外延卻縮小了,只包括了各個遊戲機、DVD機等對象。

III. 抽象層次樹不是從根部向下長的,而是從葉子節點向上歸納生成的。

IV. 某一個葉子節點所代表的對象可以歸入所有其祖先結點所代表的類

V. 直接問兩個葉子節點屬不屬於一個類沒有意義,而要指定抽象層次才有意義。例如在較低層,一輛寶馬屬於汽車,而一隻蒼蠅屬於昆蟲,不是一類。但如果指定在較高層比較,兩個都屬於具體物質,屬於一個類。

VI. 我們定義,如果一個節點CNode非葉子節點也非根節點,那麼在哲學意義上,這個節點繼承於其父節點PNode,並且說PNode是CNode的泛化。

VII. 我們定義,如果一個節點CNode非葉子節點也非根節點,如果強行將它看成其任何一個祖先節點ANode,並當做ANode使用,那麼在哲學意義上,叫做多態性。

3.3、總結

先說明這麼多了,隨著後續內容的深入,還會有更多豐富的內容進來。例如,後面會看到,所謂的"里氏代換原則(LSP)",在哲學本質上不過是在這棵樹上所加的一條限制規則,而"面向接口編程"、"低耦合、高內聚"、"依賴倒置"等一系列耳熟能詳的短語,歸結到哲學上也只是這棵樹的一些精化。

另外,看了上面的理論,我想本章開頭留下的疑問也已經煙消雲散了吧。

再提示一遍,這棵樹非常重要,得其精髓,就能理解諸多OO中概念、原則和方法的本質。後續討論中,抽象層次樹理論將作為重要的理論基礎。

真經第四章——繼承 Inheritance

"子類型必須能夠替代掉其父類型——Barbara Liskov"

4.1、原來是先有兒子才有父親

這一章我們討論繼承(Inheritance)。

我們先看一看繼承在哲學意義上時怎麼來的。對象論的世界觀認為,世界的基本元素是對象,我們將抽象思維作用於對象,形成了類的概念,而抽象的層次性形成了抽象層次樹的概念。接著,我們就可以定義:在抽象層次樹上,除根節點和葉子節點外,任一節點CNode非嚴格繼承其所有祖先節點所組成的集合中的任一元素,而CNode嚴格繼承其父節點PNode。

繼承概念,看似簡單,若深入思考,卻隱藏眾多玄機。首先,繼承描述的實際是抽象層次樹上祖先節點與子孫節點的關係,但

我個人一直不贊成使用繼承(Inheritance)一詞來描述這種關係,而推薦使用泛化(Generalization)一詞。為什麼呢?因為我們已經知道,從哲學和認識論角度來說,是先有對象,然後有類;先有子類,然後有父類,是一種自底向上形成的體系。而繼承一詞,明顯帶有自頂向下的暗示,因為往往是先有爺爺、有父親繼承爺爺、然後才能有兒子繼承父親。這樣,就容易讓人誤解成是先有父類才有子類。所以,為了更好的體現繼承的哲學本質,我更傾向於使用"泛化"代替"繼承"。當然,由於繼承一詞已經被普遍使用和接受,接下來我還是會沿用繼承一詞,只不過希望各位時刻牢記,其實是先有了子類,才從子類泛化出父類。

當然,當父類被抽象出來後,可能還會有新的子類加進來。但是,當初父類一定是從某些子類中泛化出來的,而不會是憑空突然出現的。

4.2、繼承的作用

探討了繼承的本質,然後我們來探討繼承存在的意義。一切存在的東西都是有意義的,否則就不可能存在。注意,這裡的"意義"是中性詞,指事物存在的原由,不要理解成褒義。

我們需要繼承這個概念,本質上是因為對象論中世界的運作往往是在某一抽象層次上進行的,而不是在最低的基本對象層次上。舉個例子,某人發燒了,對其他人說:我生病了,要去醫院看醫生。這句簡短的話中有一個代詞"我"和三個名詞"病"、"醫院"、"醫生"。這四個具有名詞性的詞語中,除了"我"是運作在世界的最底層——基本對象層外,其他三個都運作在抽象層次,在這個語境中,"病"、"醫院"、"醫生"都是抽象的,他並沒有在醫院裡拉著某個醫生對別人說:我生了這個,需要去這裡看這個。但是,本質上他確實是生了一個具體的病,要去一個具體的醫院看一個具體的醫生,那麼在哲學上要如何映射這種抽象和具體呢?就是靠繼承, 拿醫生來說吧,所有繼承自"醫生"類的類所指的所有具體對象都可以替換掉這裡具體的醫生,這都不影響這句話語義的正確性。

所以,繼承的哲學作用就是:規定了抽象與具體之間的可映射性。形式化一點說:設G(c1,c2)意為c1非嚴格泛化自c2,I(c,o)意為對象o屬於c的外延,其中c1,c2,c均為類,o為對象。那麼,c可在哲學語義上映射成o,當且僅當o∈{o|I(c,o)}∪{o|I(c',o) 且 G(c,c')}

4.3、開放-關閉

如果你討厭看形式化的東西,那麼上面藍色文字不看也罷,但是,有一條原則你一定很感興趣,那就是著名的開放-關閉原則(OCP)。

開放-關閉原則(OCP):軟件實體應該可以擴展,但不可以修改。

為什麼忽然扯到OCP呢?因為,OCP正是上文討論的哲學原理在程序世界的具體表述。我們來對比看一下,到底OCP是個什麼意思。

還是上面看病那個例子,什麼叫可以擴展?就是說,因為在某個抽象層次是進行表述,就不能把話說死了,不能全是這個、那個的把每個對象都指派明白。如,那句話改成"我的右腳扭到了,要去北京航空航天大學醫院去看胡青牛醫生",這句話就沒有擴展性可言了,所有話都說死了,你如果去的是北醫三院或臨沂市人民醫院,那麼語義就不對了,而如果找的不是胡青牛而是華佗或扁鵲,語義也不對了。為什麼無法擴展?因為所有點都指定了具體的對象。

而原話"我生病了,要去醫院看醫生"則擴展性很大,因為只要不違反可映射性定義,映射到任何符合條件的對象都正確。擴展性和靈活性大大提高了。所以,"可以擴展"四字從哲學上其實是要我們在設計和開發軟件時提高抽象層次,不要總在具體對象層面上進行處理。這下,你明白為什麼說OCP可以提高軟件的可擴展性和靈活性了吧。

再來說說"不可以修改",因為如果隨便亂改,那就天下大亂了。還是醫院那個例子,"醫院"這個類所映射到的對象,一定是治病的地方。如果這東西隨便改,例如明天"醫院"和"食堂"的概念對換了,那麻煩了,我們所有人都要改,要把兩個概念從腦子中對換過來,全世界的書、報紙、Internet……凡是依賴這兩者進行表述的地方都要改,那不是天下大亂麼?軟件世界中也會發生這種牽一髮而動全身的問題。所以我們提倡設計好的類一定要"對修改關閉"。

以上,就是OCP的哲學意義。

4.4、兒子,你要能完全替代老爹才行

不過,要想世界正常運作,只有OCP似乎還有點問題。到目前為止,我們都是在抽象層次樹已經存在,並且假定它完全正確的前提下討論的,可是,我們並沒有任何規則限制抽象層次樹的正確性,例如,如果我把食堂掛到醫院下,讓食堂成為醫院的子類,在理論上時沒有錯的,但如果這樣隨便亂規定繼承關係,那麼一切依賴繼承正確性的原則、概念都沒有意義了。所以,只有OCP是不夠的,需要對繼承進行一個限制。

Barbara Liskov在1987年的OOPSLA大會上發表了一篇文章——《Data Abstraction and Hierarchy》,其中提出了一個非常重要的原則,叫里氏代換原則(LSP)。

里氏代換原則(LSP):子類型應該能代替掉其父類型,且代替後程序運行情況不會錯亂。

我們還是用例子去理解LSP。

現代辦公幾乎都要用到個人計算機,個人計算機本身是一個抽象概念,臺式PC是其中一個子類。後來,發明了筆記本電腦,我們想把筆記本電腦歸為個人計算機的子類,是否合理呢?根據LSP,我們將臺式PC都替換成筆記本電腦,世界應該是照常運行的(當然,實際情況可能複雜些,有些地方不能用筆記本電腦替換,但這裡我們忽略這種差別)。我們辦公時依賴的類是"個人計算機",而筆記本電腦完全可以替代這個類型而使得世界運行正常,所以,我們說將筆記本電腦歸於個人計算機的子類是符合LSP的。

後來,又發明了轉基因黃瓜,我們也想將它歸到個人計算機的子類中去,行不行呢?好的,現在我們再運用LSP,將世界上每個依賴個人計算機的地方都替換成一根轉基因黃瓜。好的,世界人民都瘋了!明顯這種替換會令世界運行錯亂。所以,我們不能讓轉基因黃瓜繼承個人計算機。

上面的例子是顯而易見的,但有些卻不那麼明顯。例如,現在問,獸醫是醫生的子類嗎?這個問題,一下子還真不是很好回答,但我們可以LSP一下,現在,我們把醫院裡的醫生都替換為獸醫,你還敢去醫院看病嗎?嗯,這下子不用我多說了吧。

最後一定要說明的是,LSP應用於程序世界和現實世界時有很大差別的,現實世界繁雜、不確定性因素多,而程序世界簡單、確定。總之,LSP就是讓你記住一條,凡是系統中有繼承關係的地方,子類型一定能代替父類型,而且替換後程序運行要正常。換言之,繼承是一種嚴格的"IS-A"關係,也是"一般和特殊"的哲學原理在程序世界中的體現。

4.5、總結

繼承的話題就討論到這裡了。很多朋友在運用繼承時有疑惑,或不能很好的確定繼承關係,歸其根本是沒有真正理解繼承的意義。只要能理解繼承的本質意義,加上OCP和LSP的運用,是可以寫出正確的繼承體系。

真經第五章——耦合 Couple

"一隻蝴蝶在巴西輕拍翅膀,可以導致一個月後德克薩斯州的一場龍捲風——蝴蝶效應"

5.1、為耦合平反

做程序的人,往往感覺"耦合(Couple)"不是什麼好東西。經常有人、有書、有文章對我們諄諄教導:要降低耦合,要降低耦合……久而久之,好像耦合在程序界成了貶義詞,弄得我們恨不得把耦合從程序裡全部拿掉。

這誤解可委屈耦合了。要是哪天沒了耦合,這世界還真玩不轉。其實耦合還有另一個名字,叫"聯繫",試問要是世界上所有對象間的聯繫都沒了,世界還能運作麼?耦合的存在是世界演進的途徑,如果沒有耦合,世界就變成了"死世界",無法演進和發展。所以,耦合可是好東西,我們要感謝它!但是任何東西都有兩面性,過度的耦合確實會令世界的運作產生困難,所以我們提倡降低耦合,這些是後話。

5.2、形形色色的耦合

下面,我們探討各種耦合式怎麼出現的。

上一章講述了繼承,其實,繼承的概念出現後,有父子、祖孫關係的類就有了一種聯繫,這種聯繫叫做"泛化耦合"。這就是我們認識的第一種耦合。

泛化耦合(Generalization Couple):由於泛化(繼承)關係的存在,在兩個有祖孫、父子關係的類間形成的一種邏輯關聯。

然後,我們討論另一種耦合。

在文章開始,我們說對象論將對象看做基本元素,而對象中有數據和方法。在現實世界中,數據並不總是簡單數據。客觀存在一些對象,它們的數據是另一個或另一些對象。例如,一個具體的羊群,有一項數據是很多具體的羊。其中羊也是對象。當抽象成抽象的"羊群"和"羊"類的時候,這種包含關係也隨之被抽象到了類中,由此在兩個類之間就形成了耦合。

這種耦合出現的哲學基礎是,對象本身固有的包含關係,在進行事物抽象時被同時抽象到了類中。所以,我個人將其稱為包含耦合。

包含耦合又分為兩種情況,一種是被包含對象單純聚合在包含對象中,但沒有形成哲學意義上"整體與部分"的關係,這是一種相對較弱的聯繫,叫做聚合。例如,上例中羊群和羊就是聚合關係,如果拿掉一兩隻羊,羊群還是羊群。

聚合(Aggregation):一種弱的擁有關係,體現A對象可以包含B對象,但B對象不是A對象的一部分。

另一種情況是,被包含對象和包含對象形成了哲學意義上"整體與部分"的關係,如汽車和輪子,把輪子拿掉,汽車就不再是完整意義上的汽車了。這種關係叫做組合。

組合(Composition):一種強的擁有關係,體現了嚴格的部分和整體的關係,部分和整體具有一樣的生命週期。

通過上面的探討,我們認識了泛化耦合、聚合和組合三種耦合形式,最後,還有一種耦合叫依賴。什麼是依賴呢?我們知道,在對象論中,將世界的演進看成是在初始作用力下,對象之間相互調用、相互協作完成的。如果兩個類在需求範圍內,既定邏輯上存在協作的可能,那麼這兩個類就存在依賴關係(或叫關聯關係)。其實,我們常說的"低耦合,高內聚"、"降低耦合"等建議,主要是針對依賴說的。

依賴(Dependency):由於邏輯上相互協作可能,而形成的一種關係。

好的,到目前為止,我們已經認識了四種基本耦合。下面用一副圖,直觀感受一下世界的各種耦合。

面向對象的哲學體系及科學體系的探討

圖5.1、耦合示例

圖5.1展示了幾種耦合的示例。其中汽車和交通工具屬於泛化耦合,輪子和方向盤組合於汽車,汽車聚合成車隊,而汽車和司機具有依賴關係。這幅圖只是耦合的一個小片段,實際上,世界上各種對象形成了一張複雜的耦合網,正因為有耦合的存在,世界才能演進。正如馬克思主義哲學所說:聯繫是普遍的、客觀的。所以,耦合的存在,有其深刻的哲學意義。

5.3、總結

不知你是否會有這樣的疑問:文章開始,不是說對象論將對象看做相互獨立的嗎?怎麼又耦合起來了。這是矛盾的嗎?實則不矛盾。因為我們所處的境界已經不同。剛開始,我們拋開一切,忘記一切,從本質的角度用對象論去看世界,我們看到的對象是相對孤立的。而後來,我們的抽象思維作用於這個世界,所衍生出來的一系列概念,是我們的抽象能 力給這個世界抹上的色彩。就如我們用唯物主義看世界時,剛開始要拋開一切,認為世界只有"可被意識所反映的客觀實在",而後,這個物質為本的世界在我們的抽象思維中衍生出各種概念。為了讓我們更好的、系統的認識對象論,剛開始,我們拋開一切直取本質,而後來,我們要層層衍生,將拋卻的東西再找回來,在這個"找"的過程中,我們才能領會OO中的各種概念、事物其在哲學意義上是怎麼來的。

真經第六章——運作 Moving

"運動是絕對的——牛頓"

6.1、導言

在前五章中,我們從世界觀的這話題開始,逐步引出了抽象、層次、繼承和耦合。這些內容,形成了對象論中關於世界的結構體系。

然而,要想真正描述一個世界,僅有結構式不行的。開始我們說過,世界觀主要關注兩個方面:一是世界是什麼樣子的(結構),另一個就是世界時如何演進的(運作)。現在,我們來討論對象論中關於世界運作的理論。

這裡首先要指出一點,"對象論"是關於程序世界(即將一個軟件系統看成一個世界)的世界觀,而非關於現實世界的,所以,將對象論應用於現實世界時,往往會有所偏頗。其實前面的某些地方已經體現出這一點,而在運作理論這裡,會體現的尤其明顯。但是為了直觀起見,我依然會將對象論應用於現實世界去舉例子,當然我會非常謹慎和小心,並且會明確指出對象論應用於現實世界的偏頗在哪裡。

6.2、世界本沒有類

對象論認為:世界的演進,是而且只是各種對象通過互相調用其他對象的公開服務而完成交互。

注意,是對象交互,而不是類交互!沒錯,類之間是永遠不可能交互的。因為不論是現實世界還是程序世界,從來不存在具體的類。類只是抽象思維作用於對象的產物,它幫助我們理解、記憶、分析和設計。類是抽象的概念,它"客觀"存在,但不是"具體"的存在。

例如,現實世界中,我們可以找出很多個"具體的蘋果"對象,但是你能找出一個東西,說它是"蘋果"這個類嗎?你這一輩子吃的每一個蘋果,都是一個具體的蘋果對象,從來沒有具體的"蘋果類"和你交互過。再上升一點,你一生交互過的所有東西,都是對象,而沒有一個具體的類。"類"不過是你的抽象思維作用於對象形成的幫助你理解認識世界的抽象概念罷了。"類們"從不曾和你真正交互。

程序世界中也是一樣,程序運行起來,從來都是具體對象之間的交互,類只是幫助你分析設計的概念工具罷了。

認識到上面幾點對於理解對象論的世界運行理論非常重要,時刻銘記,參與真正世界運行的,只有對象,沒有類!對象在世界中,類在我們心中!

這一小節的標題是"世界本沒有類",代表兩個意思:一是世界"本來"沒有類,二是世界"本質"沒有類。

你可能會問,在第五章"耦合"中,不是說依賴關係是"兩個類因為可能交互而產生的關係"嗎?其實,確切點說,應該是"兩個類所能映射到的對象因為可能交互而產生的關係",本質上,依賴本來是對象間的依賴,只不過在抽象時被同時抽象到類裡面了。

6.3、程序世界——大同的和諧世界

雖然在對象論裡,現實世界和抽象世界的基本運作機理是一樣的,但程序世界和現實世界在具體運作上有很大差別。首先,我要告訴你,程序世界時多麼的大同和和諧!

程序世界與現實世界第一點區別:現實世界的依賴以對象為單位,程序世界的依賴以類為單位。

沒明白這意味著什麼?

舉個例子,在現實世界中,是不是關係很重要啊。為什麼?因為你認識的人多,可依賴的人就多。例如你生病了,如果你有個醫生朋友,看病就方便很多;如果你要打官司,而你又恰巧認識律師朋友,是不是很爽呢;如果你想上清華大學,剛好清華大學校長是你親戚,那一切就好辦多了是吧。

為什麼會這樣?究其本質,是因為現實世界中對象間的依賴是以對象為單位的,這種依賴關係不會隨著泛化過程而被泛化到類裡面去。例如,有一個人現在在北京航空航天大學上學,從這"一個人可"以泛化出"人"這個類,而北航可以泛化出"大學"這個類,但這個具體的人和北航的這種關係可沒有被泛化到兩個類中,也就是說,並不是每一個"人對象"都可以去任何一個"大學對象"去上學的。

不過,如果是程序世界裡,上面的推理是可行的,因為程序世界中對象間的依賴是以類為單位的,這種依賴關係會隨著泛化過程而被泛化到類裡面去。並且,只要兩個類建立了依賴,那麼兩個類之間的所有對象都兩兩依賴了。換句話說,在程序世界裡,只要有一個"人"和一個"大學"發生了聯繫,那麼這種聯繫就被泛化到類中了,隨後,所有的"人"都可以上"任何"的大學。

面向對象的哲學體系及科學體系的探討

圖6.1、兩個世界中依賴的區別

看圖6.1,假設世界上只有三個人和三所大學。在現實世界中,小龍女考上了清華,不過這和其他人其他大學一點關係也沒有,這種關係並沒有體現在類上,看,兩個類沒有任何聯繫。但在程序世界中,小龍女考上了清華,一下子人和大學兩個類就關聯起來了,接著,張無忌和郭靖這兩個不好好學習的學生也沾了光,和三所大學都聯繫起來了。(提示:其實這裡和第四章講到的OCP和LSP聯繫非常緊密,讀者可以聯繫OCP和LSP兩個原則自己思考一下為什麼程序世界會這樣。)

你知道了吧,在程序世界裡,全世界的醫生隨你看,律師隨你用,大學隨你上,美食隨你吃!多麼和諧大同的美好世界!

看了上面對程序世界的描述,你是不是已經垂涎三尺了?恨不得自己變成一段代碼,跑到程序世界裡。不過彆著急,事情也許沒有你想象的那麼美好。下面我們來看另一個程序世界與現實世界的區別。

6.4、程序世界——封建的專制世界

上文描述了程序世界是多麼多麼美好,不過如果有一天,你真的跑到裡面去了,你可就慘了。不信看下面。話說你一進程序世界,就迫不及待想在程序世界裡找個漂亮的女朋友,可以嗎?對不起,不成!你想吃法國大餐,對不起,不成!你想上最好的大學,對不起,不成!……搞什麼!不是說程序世界什麼都可以得到嗎。沒錯,除了選擇權!

程序世界裡的對象沒有選擇權。

為什麼會這樣?因為如果對象有選擇權,就沒法貫徹OCP了!你要是活在程序世界裡,不但給你包辦婚姻,連吃飯、上學……一切的一切,你都得服從包辦,對象一點點選擇權也沒有。至於誰給你包辦的,那是後話。

看了這些,你還敢去程序世界嗎?不過這還不是最恐怖的,告訴你更恐怖的一點:

程序世界裡的對象不認識對象。

沒錯,良好的面向對象提倡對象不認識對象!很不可思議?其實,這就是所謂的"低耦合",我們喊了那麼多年的"低耦合",到底什麼是低耦合?所謂低耦合,就是先剝奪對象的選擇權,再剝奪對象的感覺。對象間誰也不認識誰,只知道對象能提供什麼服務。

我們現在瞭解了程序世界是什麼樣子了,下面,我們討論程序世界為什麼要這樣。

6.5、有奶就是娘

中國有句俗語,叫"有奶就是娘",往往用來諷刺那種六親不認,兩面三刀,誰給好處就跟誰的無恥小人。不過,面向對象可是非常提倡"有奶就是娘"的行為。如果我們的程序都能做到"有奶就是娘"的地步,那就真是實現了"低耦合"這一教義了,套用梁朝偉的話,在程序世界裡,有奶就是孃的行為"是美德"。

要理解上述道理,我們要先拋卻我們腦中的道德、廉恥等概念,從本質上看看"有奶就是娘"體現了什麼哲學道理。

"有奶就是娘",純從字面解釋,是說任何一個人,只要能給奶喝,就當做自己親孃。上升到哲學層面,是說這麼一個意思:不以其他對象實體本身為交互準則,而以其他對象的行為作為交互準則,與一個對象是否進行交互純粹是從其行為判斷,而不對對象本體有任何概念。

這種處事哲學,在現實生活中是最被人鄙夷的,但在程序世界裡確是最提倡的。如果一個程序世界裡,所有對象都能以"有奶就是娘"的哲學去處事,那麼,這就是一個最美好運作方式。

6.6、接口橫空出世

上文說到,程序世界中提倡的運作方式是"有奶就是娘"的方式,但要真正實現這種方式,似乎還少點東西。我們回顧一下,世界本來只有對象,我們從對象中抽象出了類,這就是目前我們眼中的世界。這樣,我們的交互,要麼以對象為準則,要麼以類為準則。

以對象為準則,顯然是不行的,因為我們說了,對象間根本互不認識。以類為準則,理論上可行,但這樣有問題,就是類本身是對象"實體的抽象",是為了更好記憶、描述和認識世界而創建的對象,歸根到底,還是"實體"範疇的概念,所以在哲學上還是和"以行為作為交互準則"向左。

認識到以上困難,就能認識到,目前我們的世界還無法實現以行為為交互準則,於是,我們需要為世界再衍生一些內容。第二章說過,世界本身只有對象,而衍生其他概念的基本方法是抽象。所以,這裡我們當然要用抽象衍生一些概念出來。進一步,類是對象"實體"的抽象,而我們需要的是以行為為交互準則,很自然的,我們完全可以創建一種新概念,這種概念是行為的抽象,這種新概念,就是接口(Interface)。

接口(Interface):對象行為的抽象。

這裡要說明,接口和類雖然都是從對象上通過抽象衍生出的概念,但兩者本質不同,是從對象的兩個不同的哲學角度和動機,抽象出的不同概念,並形成世界兩個完全不同的方面(Aspect)。至於兩者具體有什麼區別,下一小節詳細討論。

6.7、接口 vs 抽象類

經常有朋友迷惑一件事情,抽象類和接口有什麼區別?何時使用抽象類,何時使用接口?但從功能來講,抽象類完全可以代替接口,那為什麼還要有接口呢?這一小節來分析這些問題。

這裡附帶說一個問題,產生這種疑惑的原因,大多是因為朋友們已經習慣了學習一個東西時,只看其什麼樣子?怎麼用?而不習慣於弄清楚一個東西起源於哪?出現的動機是什麼?其實,要想學好、用好任何一個東西,後兩個問題更關鍵一些。

舉個例子,有人發明了吹風機,我們如果只搞清楚其是什麼樣子——"有個把手,有個吹風筒",以及怎麼用——"打開按鈕能吹出熱風,關閉按鈕就停止了"。如果我們只搞清楚這些,那麼我們八成用不對這個東西,為什麼?因為我們根本不知道這東西是怎麼來的,它為什麼要被髮明出來。也許我們天天拿他吹臉取暖或吹衣服,還一派洋洋得意以為用的很好的樣子。殊不知這東西其實是用來吹頭髮幫助頭髮快點幹起來的。

不要笑,這種事經常發生在我們身上。因為在軟件開發中,有太多的東西,我們只顧著學習其是什麼樣子,怎麼個用法,也許就像吹風機一樣,這些並不複雜,然後我們就把它用到不該用的地方,還以為自己用得很好。

用不用得好吹風機,不在於是否熟練掌握開開關關,而在於是不是用它吹頭髮。

同理,任何東西用得好不好,不在於是不是熟練掌握用法,而在於是不是用對了地方。而要想用對地方,就要弄清楚這個東西的"怎麼出來的"和"出來是做什麼用的"。

說了挺多,我們回到接口和抽象類的話題上來。

首先要說明一點,"抽象類(Abstract Class)"和"類(Class)"在哲學意義上沒什麼區別,其區別僅僅是實現層面上的,即抽象類只不過是一種特殊的類,編程環境強制不準這種類生成實例,哲學意義上兩者沒有任何區別。所以,從哲學層面討論"抽象類與接口對比"和討論"類與接口對比"是等價的。

類與接口的不同點有以下幾點:

I. 抽象範疇不同。類是對象"體徵"的抽象,接口是對象行為的抽象。

II. 抽象動機不同。抽象出類是為了幫助記憶、認識世界,抽象出接口是為了實現低耦合交互。

III. 關注不同。類關注共同的體徵,接口關注用來交互的行為。

IV. 存在範疇不同。類存在於抽象層次樹上,接口存在於接口網。

V. 應用範疇不同。類應用於結構範疇,是靜態概念,接口應用於運作範疇,是動態概念。

上面的條目有點學術了,通俗說來,類是從對象實體的的體徵範疇上抽象出來的,用來幫助我們記憶、分析世界不同的對象,主要表明對象"什麼樣子";而接口是從對象交互時需要的行為中抽象出來的,關注對象交互時需要的行為。

還是舉個例子吧。

例如,有一群具體的司機和好多輛具體的汽車,我們可以從司機中抽象出"司機"這個類,從汽車抽象出"汽車"這個類,這種抽象是"體徵範疇"的,抽象的目的僅僅是幫助記憶、認識,完全和交互沒有關係。而當考慮到交互——司機需要駕駛汽車,於是抽象出一個"可駕駛"這個接口。注意,一但"可駕駛"這個接口被抽象出來,就完全和司機以及汽車沒有關係了,除了汽車,拖拉機、輪船、飛機都可以實現這個接口,而不一定是司機,會開車的任何人都可以通過"可駕駛"這個接口去駕駛任何實現"可駕駛"接口的東西。這樣一來,"駕駛"這種交互就完全取決於這個接口了,這就是"以行為為交互準則的意思"。

如果明白了這一小節的內容,相信大家再也不會被"接口和類有什麼區別?"、"何時使用抽象類,何時使用接口?"這樣的問題迷惑了,而可以揮灑自如的在系統中正確使用接口和類。一個方法:拿不準的時候問問自己,這個抽象是體徵抽象還是行為抽象?是為了記憶、分析、設計還是為了交互需要?想明白,再下手。

6.8、依賴是如何被倒置的

弄清楚了接口,下面可以談一個有名的OO原則了:依賴倒置原則(DIP)。

如上,我們先不說DIP是什麼,而是搞清楚DIP的來龍去脈。到時,朋友們自然對DIP就有深刻理解了。我們開始!

首先,我們要說明,依賴是有方向的,客戶類依賴於服務類。什麼是客戶類?如果A類需要B類提供的服務,那麼A類就依賴B類,反之不成立。在沒有引入接口前,客戶類"知道"服務類,而服務類"不知道"客戶類,就像下面這個樣子。

面向對象的哲學體系及科學體系的探討

圖6.2、沒有接口的依賴

我們看到,司機作為客戶類,汽車作為服務類。依賴的方向是從司機到汽車,以為這裡司機要使用汽車提供的"駕駛"方法操作汽車。這是我們不推薦的方式,因為不夠"松耦合"。於是,我們將駕駛抽象成接口,依賴變成如下形式。

面向對象的哲學體系及科學體系的探討

圖6.3、引入接口後的依賴

如圖6.3所示,我們從這種交互關係中,抽象出了"可駕駛"這個接口。注意,此時兩者誰也不依賴誰,或說誰也不知道誰了。那麼為什麼司機可以放心呢?因為他知道可駕駛接口的存在,他要駕駛的東西一定實現了這個接口,甭管是什麼,只要實現了這個接口,我就能駕駛。其實這裡才體現出接口的哲學意義。

接口的哲學意義:對客戶類的保證,對服務類的約束。

正是接口約束了服務類必須實現什麼功能,客戶類才可以在不知道具體服務類的情況下"放心"進行交互,因為接口對客戶類提供了一種保證。希望各位能好好體會接口的這種哲學意義,這對於對象論的良好運行體質的理解非常重要。

可是,這樣還不夠,我們還有一個非常重要的問題沒有討論:誰有權利定義接口?或者說服務類和客戶類誰擁有接口?當然,理論上時誰擁有都可以,但卻會對世界的運作產生巨大影響。我們先看服務類擁有接口的情形。

面向對象的哲學體系及科學體系的探討

圖6.4、服務類擁有接口

如圖6.4,由於服務類擁有制定接口的權利,所以各個服務類都定義了自己的接口,一般情況下他們的接口是不相容的。如圖,司機可以駕駛汽車,但由於輪船、飛機各自有自己的可駕駛接口,所以會開汽車未必會開飛機和輪船,如果要開飛機或輪船還要一個個學,現實世界中就是這樣一種情況。所以,這種世界的運行其實接口幾乎沒有起到作用,由於服務類是"大爺",所以它們可以指定諸多霸王條款,而客戶必須忍氣吞聲去遷就,所以,實際的依賴方向還是從客戶類到服務類。

下面在看看客戶類擁有接口會是什麼樣子。

面向對象的哲學體系及科學體系的探討

圖6.5、客戶類擁有接口

看上圖,客戶終於翻身做主人了,現在客戶擁有定義接口的權利,服務類必須無條件實現,這下好了,只要會開汽車,就會開輪船和飛機,因為客戶有權利定義一個統一的接口,服務類必須無條件實現!這樣,三種交通工具的駕駛方法必須完全一致(雖然現實世界還沒有這樣),這回客戶終於可以揚眉吐氣,體會一把"顧客是上帝"的感覺了。

在圖6.5的情況下,司機可以有權定義接口,他不必"知道"服務類,而服務類必須"知道"客戶定義了什麼接口,你有沒有發現,依賴的方向已經悄悄倒置過來了!變成服務類依賴客戶類了(誰知道誰,誰就依賴誰)!這就是"依賴倒置"的由來。不必說,所謂依賴倒置原則就是讓我們必須按圖6.5的方式運行世界,而不能按圖6.2,6.3,6.4的方式。下面正式定義依賴倒置原則。

依賴倒置原則(DIP):客戶類和服務類都應該依賴於抽象(接口),並且客戶類擁有接口。

我想,看過上述來龍去脈,已經不用我再去解釋這個原則了吧。

6.9、神秘的統治者

到目前為止,我們基本已經搞清楚了對象世界的運行機制。但仍有一個疑問:我們曾經說過,程序世界裡對象時沒有選擇權的,甚至不知道誰是誰,只知道接口,那麼,誰來指定服務類呢?

例如,上述司機可以制定接口,所以汽車、飛機、輪船等可駕駛的東西都要實現,於是司機可以按照自己制定的方式駕駛東西。但是,司機不能選擇駕駛什麼啊,他根本不知道自己駕駛的是什麼,那麼,誰制定他是駕駛飛機、汽車還是輪船呢?

似乎冥冥中,這個世界存在一個統治者,它掌管所有對象之間誰和誰交互(只要不違反接口),否則,世界根本沒法正常運行。沒錯,程序世界是有這麼一個統治者,他就是大名鼎鼎的"依賴注入容器(DI)",也有人叫做"控制反轉容器(IoC)"。

什麼叫依賴注入?什麼叫控制反轉?如果你看了上面的文章,那太好理解了,依賴注入就是容器挑選符合接口的服務類為客戶類提供服務。例如,上面司機要一個可駕駛的東西,容器就會根據既定規則選擇一個,可能是飛機、可能是汽車、也可能是輪船,交給司機。司機駕駛就行了,不用管是什麼,反正知道這東西肯定實現了"可駕駛"接口。

讓我們向這個偉大的統治者致敬吧,沒有他,程序世界可真玩不轉了(當然,如果某個程序世界不符合DIP甚至沒接口,都是類之間依賴,那麼就不需要依賴注入容器了,不過這麼一來,可就是"高耦合"了,是OO所反對的)。

6.10、運作起來吧

到了這裡,根本不用我廢話說程序世界時怎麼運作的了,因為上面都已經說明白了。不過,我還是用短短几句話總結一下吧。

一個符合OO原則的、低耦合的程序世界的運作形式是這樣的:首先參與運作的本質只有對象,對象不直接依賴,沒有選擇權,互相不知道,而只知道各個接口。客戶類制定接口,對象間通過接口交互,形成運作。世界的統治者依賴注入容器決定選擇哪個服務類給客戶類使用。

好了,關於程序世界的運作哲理就講到這裡了,大家可以在腦子裡描繪一下上述運作情景,加深印象。


分享到:


相關文章: