August 11 14:25~18:25
▲牛眼的世界和人眼的世界是同一個世界嗎?
8/17、24這兩天晚上要上「敏捷開發懶人包:物件導向技能」,之前寫過幾篇介紹物件導向技術的文章,包含:
- 什麼是物件導向(1):簡介
- 什麼是物件導向(2):Object, Class, Instance
- 什麼是物件導向(3):Polymorphism
- 什麼是物件導向(4):Inheritance
- Cohesion and Coupling(耦合與內聚)
- Open-Closed Principle(開放封閉原則)
- Single-Responsibility Principle(單一責任原則)
- Liskov Substitution Principle(里氏替代原則)
- Dependency-Inversion Principle(相依反倒原則)
- SOLID:五則皆變
不管有沒有報名上課,對物件導向有興趣的鄉民都可以參考。今天要談一個更根本的問題:「物件導向到底是什麼?」。
***
一種看待世界的方法
人生在世,對於世界是由什麼所構成?我到底是什麼?什麼是存在?這類的問題一直抱有各種不同的看法。從東方哲學來解釋,佛家說:「色即是空。空即是色。」、「凡所有相,皆是虛妄,若見諸相非相,則見如來。」所以法師才會說:「都是假的、眼睛業障重啊」。
老子的《道德經》也提到:「道可道,非常道;名可名,非常名。無,名天地之始;有,名萬物之母。」總之世界剛開始的時候是混沌一片,是人擅自將其取名,有了區別心之後才形成現在的樣子。
持這種觀點來看待世界,修行到最後變成沒有分別心,達到天人合一的境界。
以上所說好像太不科學,沒辦法解決日常工作中所遭遇到的問題。有的人認為應該用邏輯或數學角度來解釋世界。也有人覺得人類觀察到的世界背後冥冥之中似乎有著一定的結構存在,只要找出這些結構就可以知道世界的真像。也有人認為這世界根本沒所謂什麼對錯可言,一切都是人的觀點不同所形成的看法而以。只要尊重彼此的看法,就不會引起爭端。
有點扯得太遠,拉回到今天的主題。
▼物件導向也是一種看待世界的方法,在它的眼中世界由一群彼此互動的物件(object)所構成。藉由這群物件,可以在電腦中模擬真實世界的狀況,用以解決真實世界的問題。
物件是一個擁有狀態(state)與行為(behavior)的實體(entity)。就好像真實世界中大部分的物體或東西一樣,外界可以藉由激發物件的行為來改變它的狀態。例如,按下手機開機按鈕(激發行為、執行某個動作)手機的螢幕就亮起來(狀態被改變);人吃了早餐(行為)肚子就飽了(狀態)。
***
問題太難怎麼辦?
假如要解決的問題很簡單,例如儲存一筆電話號碼,只需要用一個PhoneNumber物件就可以搞定這個問題。但實際上我們要解決的問題不會那麼小,都是很大的問題,例如:「電子商務系統」、「客戶關係管理系統」、「隨選視訊觀賞系統」。這時候就要透過一些技巧來幫助我們解題。
我們都聽過「團結就是力量」這句話,一根筷子很容易折斷,但要折斷一整把筷子就很困難。這個故事告訴我們,如果你的問題很大(一整把筷子)就很難對付,但是只要把這個大問題拆成小問題小到只有一根筷子的大小,就很容易解決。這種解題方法叫作「分治法」或「分而擊之」(divide and conqure)。這個方法包含兩個步驟:
- 將大問題拆成小問題。這個步驟稱為分析(analysis),是一種由上而下(top-down)的過程。
- 每個小問題解決完畢之後將其結果往上合併,最後形成整體的答案。這個步驟稱為合成(synthesis),是一種由下而上(bottom-up)的過程。
***
怎麼分?
以上所說都是很基本的觀念,接下來要討論一個稍微花點腦筋的議題:一個大問題要怎麼分?
怎麼分是屬於模組化的問題。問題切割的好,到達「庖丁解牛」的最高境界達到;切的不好,就好像「政府都更強拆民宅」一樣,弄得天怒人怨。
切割問題可以從很多層面來談,以下舉幾種常見的作法:
- 從需求領域切割:將需求分成不同的需求區域(requirement area),例如將電子商務系統切成庫存管理、銷售、會計系統、客戶關係管理、物流管理等。這種切法有點類似前幾天介紹的領域驅動開發將Domain切成Subdomain。
- 從功能切割:由大到小針對每一個需求區域列出使用者所需要的功能。這些功能可以用use case或user story來表達。
- 從架構切割:將整坨軟體架構用Client-Server、Layered、Plugin、Pipe and Filter、SOA、Microservice等軟體架構加以切割。
- 從概念面切割:需求與架構都切好之後,要準備進入設計階段。因為我們討論的是物件導向技術,所以設計階段會切割出(產生)很多物件。每一個物件用來代表問題領域的一個概念。
不管採用哪種切割方式,我們怎麼知道這一刀切下去切的漂不漂亮?針對每一個切割出來的模組,希望可以具備很高的內聚性(Cohesion)。內聚性從高到低有好幾個不同的等級,我們的目標是功能內聚,也就是「模組內的每一個元素都是為了達成相同功能而存在模組之中」。滿足這個要求內聚力就高(模組內的成員團結一致),反之內聚力就低。舉個例子,一個國家內的人民分成假統、真統、假獨、真獨、亦獨亦統、非獨非統、非非獨非非統、維持現狀這幾派。可想而知這個國家的人民就像一盤散沙一樣,內聚力(向心力)極差。
了解內聚力之後,要追問另一個問題:為什麼切分模組的時候要達到高內聚力?有什麼好處?
- 內聚力高的模組容易理解。試想一個排序(sorting)的函數比較容易理解,還是「排序 + 壓縮 + 列印 + 將資料存到資料庫」的函數比較容易理解?
- 容易理解也代表著容易修改與擴充。
- 內聚力高的模組「抵抗改變」的能力比較強,因為模組中的成員都是為了達到相同目的而存在,所以他們只會因為一種原因而改變,那就是原本的目的變了。這就是〈Single-Responsibility Principle(單一責任原則)〉所要傳達的目標。
- 自給自足,容易獨立佈署以及在不同的情境(context)中重複使用。
***
怎麼合?
最後一個問題,切割出高內聚力的模組之後要怎麼讓這些模組合起來一起完成工作?假設一個國家的地方自治非常成功,每一個縣、市都是高內聚力模組,大家都想獨立,你不理我、我不理你,這個國家的整體性就被破壞。所以模組之間必須要存在某種「關係」,讓它們可以互相認識、互相溝通、互相合作。這種關係稱為耦合度(coupling)或相依性(dependency)。
耦合度或相依性要高還是低比較好?當然是低比較好。成年之後還死賴在家裡跟父母拿錢花用的「啃老族」,就是一種小孩跟父母耦合很高的例子。耦合度越低的模組越不會被其他人給「帶屎(拖累)」,不會因為其他模組的異動導致自己也要跟著改變。
模組化的目標希望達到高內聚力、低耦合度。耦合不能完全消失,否則所有模組各自獨立,沒有任何關係,也無法一起合作完成一件大工作。耦合度也不能太高,否則模組之間互相影響,引發牽一髮而動全身的漣波效應(ripple effect)。在設計上要達到鬆散耦合(loose coupling)的效果,最典型的做法就是採取抽象耦合(abstract coupling),也就是GoF書中提到的「programming to an interface, not an implementation」。透過介面開發系統就是一種抽象耦合,可以替換介面實作而不會影響到使用介面的人。
***
結論
這一篇無意中寫得有點長,最後節錄重點:
- 物件導向是一種看待世界運作的方法,將世界看成由一群彼此互動的物件所構成。
- 物件是一個擁有狀態與行為的實體。
- 我們用電腦所要解決的問題發生在真實世界。
- 物件導向技術藉由在電腦中執行物件的行為以及物件所保存的狀態來模擬與解決真實世界的問題的方法。
- 真實世界的問題很大,可以透過分析技巧把大問題切成小問題來處理。
- 每一個切割後的小問題必須往上合併以表達原本所要解決的大問題。
- 切割問題的結果希望能夠得到高內聚、低耦合的模組,以便作出容易理解、修改、擴充、對抗改變、重複使用且可獨立佈署的軟體。
***
友藏內心獨白:這一篇也寫太久了。
版大的文章每篇都太有寓意了
回覆刪除