2009年4月28日 星期二

Why Interface? 下集: 海大王的堅持

接著進入主題了,也是解釋為何我如此感嘆的原因。
我的報告結束後,一位海大的王教授,就叫他海大王吧!對我的設計提供了諸多指教。
我們來看看他說了什麼:

海大王觀點下的問題
CustomerMgr 跟 HotelMgr 你用 UML 裡面的 Component 圖示來表示, Component 怎麼可以被繼承。

看來這位平常都不怎麼發表意見的海大王,今天突然變得很精確,看來今天是衝著我來的。
首先我同意他的講法, Component 不會被繼承,沒有人在繼承一個 EJB 的 jar 檔吧?應該是繼承這個 jar 裡面的某個 class ,其實我想表達的也是這個意思而已,但我這樣畫你就看不懂我的梗了喔?應該不至於這麼沒有慧根吧?
我最重要的梗是:「不要為了 Referential Integrity 而改 CustomerMgr ,而應該 extend 他」,這個這麼重要的梗竟然看不到,反而只看到表面的瑕疵?
如果是黃阿公看到一定會很開心的說:這就是我這學期一直強調的 Open/Close Principle 。

海大王堅持
Component 去用 Component 外的東西一定要透過 Interface , Component 被用,也應該透過 Interface ,你的 Interface 在哪裡?

然後我解釋後,他 Repeat 他對 Interface 的堅持,感覺他頗激動,不知是什麼動力讓他如此激動地堅持他的膚淺?

我想他應該是在批評我 ChildCustomerMgr 直接去用 Subject ,沒有透過 Interface ,還有就是批評我 DeploymentMechanism 頭上的那兩條虛線箭頭,沒有透過 Interface 。
我跟他解釋, Subject 跟 Observer 我都假設他屬於 Framework 的一部份,可以直接用,不需要透過 Interface 。

用 Framework 可透過 Interface ,可透過繼承 Framework 中的 Abstract Class ,也可直接用 Framework 裡的 Concrete Class 。
這邊講的 Framework 是比較廣義的 Framework ,如 .Net Framework , Spring Framework , Struts Framework ,狹義的 Framework 應該是比喻成 Abstract Class 比較恰當。

但這位海大王,真的非常堅持 Component 要用 Component 以外的東西一定要透過 Interface ,這樣的意思就是:
我的 Component 要用 .Net Framework 裡面的 string 要透過介面。
我的 Component 要用 Java 的 LinkedList, TreeSet, TreeMap 也要透過介面。
Java 本身就提供 Subject 這種 Class 給我們使用,在 Java 叫做 Observable ,他是一個 Concrete Class ,我難道要用 Java 本身提供的東西或 .Net 本身提供的東西都還要透過 Interface ?如果是這樣,應該寫不出任何程式來吧?

我真的笑了...
好一個死讀書的海大王阿!
課本怎麼說的倒是不加思索直接輸入腦袋裡了,也不思考一下課本是不是真的想傳達那個意思給你。

至於 DeploymentMechanism 頭上的那兩條虛線,需不需要透過 Interface ,我覺得無妨,反正我之前提過了, ChildCustomerMgr, ChildHotelMgr, HotelMgrObserverCustomer 這三個 Class 都是 Deploy 的時候,由 Container Gen 出來的,而 DeploymentMechanism 也是 Container 的一部份,大家都自己人,幹嘛還透過 Interface 。

而且,透過這樣 Business Technology 分開的設計,以及 Code Gen 的機制,我真正寫 Code 的時候我只會看到 Business 框框裡面的那三個元件,完全符合海大王的要求,要透過 Interface ,只有到 Deploy 的時候, Container 才會幫我把 Technology 框框裡面的 Code Gen 出來,因此 Developer 、 User 、 Tester 都感覺不出有下面那個框框的存在,那我下面框框的部分有沒有透過 Interface 到底影響到誰了?應該不會有影響吧?


Why Interface?
海大王對 Interface 非常堅持,但他到底有沒有仔細想過,使用 Interface 帶來了什麼好處?
我覺得有必要說明一下 Interface 的用途,避免海大王迷思未來持續上演。

Interface 在 Component Base Software 上,最明顯的好處之一就是:讓你的元件可以拆下來,換成一個新元件,只要新元件的 Interface 一樣,就隨插即用。

Component Base Software 的梗也就是這個,這學期快完了,三位助教努力的在台上表演,但我卻一直沒聽到他們提這個最重要的梗,其實也讓我頗心寒的。

參透不深者一講到 Component Base Software 一定會講:「好處是 Reuse ,因為同一個 Component 寫完了以後可以賣給別人 Reuse ,也可以給下一個專案 Reuse 。」這是大部分學生的答案,因為老師課本就這樣教,他們也就像海大王一樣不加思索直接輸入腦袋裡了。

但實務上,業界那個 Component 被 Reuse 了?尤其是 business component ,根本連個鬼影子都看不到。

我認為 Replaceable 才是 Component 最重要的梗,因為 Replaceable 超越了傳統 reuse 的思維!
我引述一位前輩講的話來說明,什麼叫做超越傳統 reuse 的思維,這個前輩叫做高煥堂,幾年前有幸聽他幾堂課,給了我一些指點,讓我早幾年參透一些事情,他說:

汽車是一個系統,輪胎是這個系統裡面的一個 Component ,我今天輪胎壞了,我去買一顆新輪胎來換,換了輪胎後,我 Reuse 了整台車。

想想看,如果今天輪胎是不能拆下來的,輪胎壞了你車還能開嗎?是不是整台車就丟了?
再買一台新車?為了一個破輪胎買一台車?真是極度不合理阿!

所以在真實世界,被 reuse 的不是 component 本身, component 是被換下來淘汰的,真正 reuse 的是沒被淘汰的其他部分。

不只 component ,只要你覺得未來可能會需要被換掉的東西,最好都要透過介面來使用他,隨便翻個 Design Pattern 就會發現,都在講這個梗。

因此,什麼時候要透過 Interface 呢? Interface 使用的時機呢?當然就是你 suppose 那個東西在不久的將來會被換掉,為了換掉的時候不造成太大的痛苦,給他個 Interface ,讓他未來比較容易被換掉。

那你的 Component 使用 .Net Framework 或使用一些 J2EE Framework ,或你自己寫的 Framework ,的時候要不要透過 Interface ?就看你未來會不會把他換掉。

基本上換掉的機會不大,因為 Framework 構成了你系統的骨幹,要換通常工程浩大,所以 Framework 通常是被 Reuse 的,而且是被重度依賴的,如果用 Framework 都還要透過 Interface ,那也太多 Interface 了吧,而且最終總要用到幾個 Concrete Class ,要不然誰幫你把物件 create 出來阿?(高手應該聽得出來我在影射那個 Design Pattern 吧...)

因此海大王堅持用 Framework 也要透過 Interface 似乎是...我真不知道他在想什麼...

那你今天用了 .Net Framework 有辦法做到不改一支程式,換成 J2EE 嗎?其實也可以啦,但是就不是 Interface 可以幫你的了,有興趣請參考 Model Driven Architecture ,黃阿公有開課,作法就是用 Code Gen 的方式。

你有沒有注意到跟我的設計又有點符合處?又是 Code Gen !因為要做到 Plug and play ,除了 Interface 這招以外, Code Gen 也是一招。
有沒有看過類似的科幻電影,一個怪物手斷了,他就從地上檢起一隻備用的庫存手,靠近他的斷臂,說也奇怪,斷臂跟這隻備用手竟然長出骨肉連了起來,然後就可以用手了。
OK...以上科幻電影是我虛構的,但...有點想像力嘛...

這也是我前面所說的 DeploymentMechanism 為何不需要透過 Interface 來使用 ChildCustomerMgr, HotelMgrObserverCustomer ,因為他用 Code Gen 這種方法來完成 plug and play 阿!我還需要 Interface 嗎?

也因為 Code Gen 的機制,一個 EJB Component 可以保持 Container 的中立性,也就是說,今天你寫的 EJB 可以 Run 在 WebSphere ,也可以 Run 在 WebLogic ,也可以 Run 在 Jboss ,因為你 Deploy 的時候, Container 才會依照他的方式,把你留給 Container 發揮的程式碼 Gen 出來,每個 Container Gen 出來的 Code 不盡相同,但都 extend 你的 Component 。

跟 MDA 的 Code Gen 比較不同的是, MDA 的 Code Gen 比較絕,全部幫你 Gen 出整個系統來,當然,理論上。 EJB 這種 Code Gen 機制則只幫你 Gen 出你懶得寫的部分,比如說 Transaction 控制、 Persistence Code 、 Referential Integity Code,等等...
但目前為止,應該還沒有一個 Container 有 Gen Referential Integity Code ,應該是未來 Container 的一個發展方向。

另外,嚴格的說, EJB Component 還是會留一個 Interface 給 Container 所使用,讓每個 Container 都能以相同的介面來管理這些 Container ,但如果沒這些介面,應該也 OK ,還是可以做到 Plug and play ,只是管理 Component 的方法會變得比較千奇百怪,不統一,也比較容易造成 Container 的品質不一致,所以這裡也引出了 Interface 的另一個用途,「可以用來規範 Implementation 」,但這個用途就沒促成 Replaceable 那麼重要了。

總結

其實上課的第一天,海大王對平行四邊形應該要繼承四角形還是繼承矩形跟我就有不同的見解,他的答案就是:應該要看幾何學的定義,平行四邊行屬於四角形還是矩形。
我那時候心裡就 Oz :哇!這真的就是某八股物件導向書籍的講法阿!恩﹍這個老師應該有看一些書,四年前的我,搞不好也會講出這麼八股的話。
我的答案是:要看現況被繼承成什麼鬼樣子了,如何設計,才能產生最少的 Side Effect 。總不能為了達成完美幾何學,要我改寫大半個系統吧?

唉,我想我該解釋的都解釋了,比較感嘆的是,如果一個教授做學問都只會照著課本講的堅持,而不去思考 Why ,那教出來的學生呢?
如果海大王真的瞭解課本說的,要透過 Interface 是為了什麼?或許今天也不會那麼激動的堅持他的膚淺吧!我也沒有必要在這邊感嘆到兩點半才睡。

Why Interface? 上集: Homework - Observer Design Pattern

今天真是令人百感的交集一天,感嘆台灣軟工界師資水準的低落。
我想,走軟體這一行,還真的是業界才找得到比較深的人。
一位前輩在我準備回到學校前就告誡過我:「學軟工,還是在業界學得比較多,畢竟軟工重實務,非理論。」
其實他說的沒錯,要多做,才會參透,才會瞭解理論的梗倒底在哪裡。

今天的 Component Base Software Engineering 的課程,我上台報告我的 Homework ,如何用 Observer Design Pattern 來解決 Referential Integrity 的問題。

問題

什麼是 Referential Integrity 呢?舉例來說:我們有兩個在設計上相互獨立的 Component ,一個叫做 CustomerMgr ,一個叫做 HotelMgr ,CustomerMgr 裡面管了一個資料叫做 Customer , HotelMgr 裡面管了一個資料叫做 Reservation , Reservation 裡面有 Customer 的參照存在,當 Customer 資料被刪除時,希望相對應的 Reservation 資料也能做 delete cascade 或 update cascade 的動作。
如下圖:



前提是兩個 Component 在設計上要相對獨立,不能相依,請問該怎麼搞定?
元件架構如下圖:



在某本書上的大師提到,這個問題,可以用 Observer Design Pattern 來解,於是,這就成了我們的作業。

Observer Design Pattern 的詳細內容就不提了,基本上在實務上就像軟體上有個按鈕,不是會給他 Add 一個 Listener 嗎?然後按鈕被按了, Listener 就會聽說按鈕被按了,趕快去做一些事情,詳細內容請參照 Gof Design Pattern。

First Cut

我的第一個設計圖如下:


最簡單的解決方式就是 CustomerMgr 去 inherit Subject ,讓 CustomerMgr 擁有成為 Subject 的能力, HotelMgr 去多 realize 一個 Observer interface ,讓 HotelMgr 擁有 Observer 的能力,然後,把 HotelMgr attach 到 CustomerMgr 上,這樣 CustomerMgr 如果有變動,就可以通知到 HotelMgr 去做變更了。
他們實際上怎麼運作?請看以下循序圖:

當 CustomerMgr 元件的 deleteCustomer 被呼叫到的時候,除了刪除 customer 資料,還會去執行 notify method , notify method 裡面再去執行 Observer 的 update method 。


我們之前 attach 到 CustomerMgr 上的 Observer HotelMgr ,於是被呼叫 update method ,然後把自己的 Reservation 資料 delete cascade 掉。

以上應該就可以交差了,我想老師要的應該也只是這樣就夠了,但是我總覺得這樣的設計有損我的專業,應該再把他設計完美一點。

誰應該把 H 貼到 C 上?

我想到的第一個問題是,誰來把這兩個元件貼在一起?不可能是 CustomerMgr 把 HotelMgr 貼在自己身上,也不可能是 HotelMgr 把自己貼到 CustomerMgr ,因為這樣都會造成 CustomerMgr 與 HotelMgr 的相依。因此我覺得這件事情應該交給 Container 來完成,而且是 Deploy 的時候,依照 Deployment Descriptor 完成註冊,這樣會比較合理,所以我修改設計圖如下:

我簡單的描述了一個 DeploymentMechanism 的程式,兩條 dependency 的線代表 DeploymentMechanism 認得這兩個元件,而且會幫他們完成註冊,但在真正的實務上,這兩條線應該不會存在,因為 DeploymentMechanism 應該會透過讀 Deployment Descriptor 來認識這兩個元件,進而將這兩個元件貼在一起,不會真的直接在 Code 裡面認識這兩個元件,可透過 Java Reflection 機制達成。但我不畫那兩條線也怪,會讓人感覺沒有關連的東西,怎麼有辦法對兩個元件做動作,所以還是畫了那兩條線,避免又要解釋什麼叫做 Deployment Descriptor ,什麼叫做 Java Reflection ,然後兩個搭起來怎麼讓 DeploymentMechanism 運作。

把問題複雜化

接下來,還有一個問題是,如果今天 CustomerMgr 會發佈一個以上的 Subject , HotelMgr 會有一個以上的 Observer 該怎麼解?套用目前的架構,是無法解決的,因為目前的架構只支援一個 Component 可有一個 Subject 及一個 Observer 。
問題的根源在於目前的架構是 CustomerMgr 直接繼承 Subject 來用, HotelMgr 直接實作 Observer Interface 。
為了打破這個僵局,我們必須放棄使用繼承的方式來讓 CustomerMgr 有 Subject 的能力,因此我改而採用 Aggregation 的方式,讓 CustomerMgr 來使用 Subject ,這樣 CustomerMgr 就可以有多個 Subject 可以發佈。
HotelMgr 則改成,不直接實作 Observer 介面,而由額外的 ConcreteObserver 來實作 Observer Interface ,這些 ConcreteObserver 接到異動通知,再去通知 HotelMgr 做相對應的資料刪除或修改。
因此架構會修改如下:

CustomerMgr 元件會有一個 Subject ,這個 Subject 是 customerSubject ,未來如果 CustomerMgr 元件有另一個東西要發佈成 Subject ,只用再加一個 Subject 到 CustomerMgr 元件上就可以了,比如說, Company 資料也要被發佈成 Subject ,那就再多個 companySubject 給 CustomerMgr 。

此外, HotelMgr 這邊,可以實作多個 Concrete Observer ,比如說 HotelMgrObserverCustomer 用來監聽 Customer 資料的異動,若也要監聽 Company 資料的異動,就再實作一個 HotelMgrObserverCompany 的 Concrete Observer ,這些 Concrete Observer 收到通知後,則分別去對不同的資料進行刪除或更新的動作。

Separation of Concern & Open/Close Principle

最後,我覺得設計到現在,我們加了很多跟 pure 處理 business 不相干的設計進去,這樣做容易造成 business component 的污染,依據 separation of concern 原則,若可以將解決 business 相關的設計,與解決 referential integrity 問題的設計分開,應該會更好。

依據 open/close principle ,我們若希望能夠修改 business component ,讓他擁有解決 referential integrity 的能力,又不希望去修改 business component 本身,有一種做法就是 extend 他來改。
因此我將架構修改如下:

我們可以把處理 Referential Integrity 的程式往 Child Class 移動,讓 CustomerMgr 專心於 business 相關的程式即可,處理一些技術相關的問題,例如: transaction, persistence, referential integrity 等,技術相關的程式都往 child class 移動。
如果有玩過 EJB 的人,應該很容易就看出我的梗了,事實上 EJB 也是利用這種方法來實作,而且 EJB 會在 Deployment 的時候,才把技術相關的 Code 產生出來, Deploy 到 Container 上,因此,我認為, ChildCustomerMgr, ChildHotelMgr, HotelMgrObserverCustomer 這些 Code 也應該仿效 EJB ,在 Deploy 前再 Gen Code 出來,不應該由人來撰寫。人只用撰寫好 Deployment Descriptor ,然後 Container 讀了 Deployment Descriptor 後, Gen 出 ChildCustomerMgr, ChildHotelMgr, HotelMgrObserverCustomer 這些程式碼,而且把該貼在一起的貼在一起。
好,我的設計就到此告一個段落,再下去就太複雜了。

接著可以講今天的主題了,不過好像該分段了,有人說我的 Blog 都不分段, Readaility 很差。

2009年4月23日 星期四

兩間公司的經驗與回憶

2003/6~2008/8 我在傳統產業的系統發展組待了五年多的光陰。
2008/8~2009/2 我在高科技產業的企業 E 化部門待了半年。

兩種產業的文化差異讓我印象深刻,當時一直想找時間把我的心得寫下來,但一忙就是半年多過去,一直偷懶沒動鍵盤。

現在我人離開職場,回到學校唸書,剛考完 Software Design Method with MDA 的 Midterm ,等著上下一堂課 Agile Method ,我想該可以讓我回憶一下了。

先談一下我在兩家公司的背景:
M 公司與 L 公司。

M 公司是一間傳統的汽車工廠,員工曾經 3000 人,以生產日系的轎車與商用車出名,在台灣的市占率排名第二。 M 公司在 2003 年時大舉徵才,很多年輕的資訊人員因此有機會進入該公司。當時高科技公司才剛從 2000 年網路泡沫化的傷害從谷底慢慢爬起,傳統產業因受傷較小,因此氣勢比當時的台積電還旺。全盛時期資訊人員約 90 人,我就是在那公司的全盛時期進入資訊部,系統發展組,負責軟體系統開發的工作。
即使是傳統產業,也必須 E 化。因公司賺錢,自然也不吝惜於資訊系統的投資。一個接一個的大型專案等著我們去挑戰,大筆的預算讓我們花。我剛出社會,原本也不知道自己的方向,因為進入 M 公司,接觸了大型的軟體專案,體會了軟體開發的美麗與哀愁,從此與軟工結下緣分。
好景不長,越過了頂峰,整體汽車市場開始走下坡,市占率第二名與市占率第一名的差距持續擴大, 2006 年卡債風暴首先衝擊汽車市場,接著 2007 油價大漲,鋼價大漲, 2008 年金融海嘯,公司的股價終於跌到 10 元以下,也開始虧損。

我大概是在金融海嘯掃過來前幾個禮拜從 M 公司跑到 L 公司任職,擔任系統分析師的角色。
L 公司是一間年輕的軟體公司,主要研發的是影音多媒體相關的軟體,在影音多媒體軟體上具有國際競爭力。

M 公司與 L 公司的文化差異極大,做軟體專案的方式也有所不同,讓我這個第一次換工作的人感受很深,以下一一比較兩者差異:

員工人數
M 2000~3000
L 500~700
M 公司因為是製造業,員工明顯比軟體業多,我剛進公司的時候是 3000 人,我離開的時候不知道還有沒有滿 2000 人,公司的人數一直減少,業務也隨著市場萎縮。
L 公司則還繼續的成長中,我進去的時候幾乎每天都會收到歡迎新同仁的信件,一直到金融海嘯大浪來襲,才停止徵人。

員工素質
M 主要由大學,研究所組成。部中資訊本科系出身的人算少數,缺乏科班出身的共同語言,溝通上很困難,常常講一些覺得很基本的東西,很多人都還是聽不懂。有時不禁感嘆是我來錯單位,還是這些人來錯單位。
L 主要由台清交研究所組成,不少主管有博士學位。員工素質明顯比 M 公司高,可算是斯巴達精兵。這些天才,不只頭腦聰明,也多才多藝,尾牙時我們沒有請明星來表演,但員工的演出都有相當的水準,讓人吃驚。且由於大家都是念資訊出來的,我可以盡情的使用專業術語跟大家溝通,不需要把半獸人語,翻譯成人話,直接講半獸人語就可通。

員工工作態度
M 公司大部分員工工作都很努力,但多多少少還是有一兩隻米蟲,等退休。大家也算是很拼,但每天晚上留最晚的也 Always 是那幾個人。早期生意好的時後,大家也是在公司呆很晚,但還不至於每天超過 12 小時待在公司。
L 公司則另人訝異的竟然找不到一隻米蟲,也可能是我待得不夠久,所以沒發現。在這裡,幾乎每個人平均都九點後才會下班,一些研發部門的人聽說還會到 1 點 2 點,禮拜六還會來上一天班。員工負責任,肯吃苦的工作態度已經形成一種文化,不知動機為何?我還不清楚。
不僅如此,對工作的積極與熱情也非 M 公司所能相比,常常一開會大家意見不斷,熱烈討論到忘了時間。 M 公司敢說話的也 Always 是那幾個人,有時候還要強迫每個人輪流發言。不過 L 公司開會常常超過時間也並非好事,久了容易讓浪費青春。也可能是在這裡,跳躍式思考的人比較多,容易馬上跳到太深入的議題,較不容易控制會議。

員工平均年齡
實際數字我不知道,但我在 M 公司作了五年,還是部門裡面最年輕的男性,因為五年來都沒補年輕的新血。換到 L 公司時,才發現自己已經年紀一把了,唉...

女性同胞
M 公司的女性同胞操勞度明顯的沒有 L 公司的嚴重,因此同年齡的女性同胞, L 公司外表上的年齡大概比 M 公司大個 5~10 歲。

做事情的方法

M 公司比較保守,但若真的要做,就是誓死達成任務。允許 Delay ,但只允許調整一次 Schedule ,允許員工犯錯,但同樣的錯不能犯第二次。強調成本與效益分析,沒有效益的事情不做。
L 強調危機與變革,非常樂意去嘗試新的技術與觀念,但往往不惜成本。大家都想要把事情做到最好,但成本與效益是否平衡?似乎比較不重視。

對軟體系統的要求
M 公司做軟體系統的目標是以最少的人力與成本,勉強達成所有使用者需求即可,系統可以說是醜的可以,不過實用度絕對夠。
L 公司則非常的重視 Usability ,不惜成本,要讓使用者用最直覺的方式使用系統。

工作步調
M 公司說要幹一件案子,如果是專案等級的案子,通常從說要做,到開始真的要做,都會有一段時間的高階討論與策略方向的訂定,這些討論通常是使用者單位的主管與資訊單位的主管會先進行初步的規劃,達成初步的共識,成本效益分析也是在這段時間進行,若專案真的要開幹,使用者單位的承辦人,必須撰寫立案簽呈,明訂專案的願景、目標、效益,會簽資訊單位,經雙方主管同意後,專案才會正式成立。通常從說要做,到真的開幹可能都半年了,因為流程上的歧見可能就要喬很久,若跨部門,可能要喬更久,若流程確定了,之後軟體開發才不會有太大的變化與風險。
L 公司的步調極快,高階的一封 E-mail 可能就代表專案要 Kick-off 了。看不到成本效益分析,直接排入工作項目,一有 Resource 就開幹了, Project Leader 或 SA 出馬,開始去找使用者訪談。

使用者單位的涉入程度
M 公司的案子是使用者單位立的,資訊單位的角色是幫助使用者單位達成專案,因此主要成敗責任在使用者單位,當然績效也會被使用者單位瓜分大部分走。使用者單位的態度積極,負責人先建立內部共識,瞭解該單位的需求,甚至先做完部分非正式化的流程分析,再一併與資訊單位說明。因此需求是先被使用者單位先統合過一次的,專案過程中的歧見因此減少很多。使用者也比較清楚他們想要的是什麼東西,在不清楚要的是什麼之前,專案是無法 Kick-off 的,因為前面有說過, Kick-off 前立案簽呈必須被通過,包括願景、目標、效益都必須被寫出來。
L 公司的案子看不出這個案子是誰負責的?使用者單位的代表感覺起來就是使用者,而非身負重任的專案承辦人。使用者單位並沒有一個統合的窗口,代表所有的使用者的意見,因此常常會遇到一個問題:「 A 這樣說, B 說另外一套,這時候到底要聽誰的?」在 M 公司不可能會發生這種事情,若有,資訊單位則會把使用者單位的承辦人找來,請他把自己人搞定,再來告訴我們資訊人員怎麼做。

制度
M 公司算是一間相對有制度的公司, L 公司相對就比較沒制度,我在 L 公司沒看過什麼叫做公文、簽呈、工聯單這類的東西,都以 E-mail 取代。有的是一些功能性的表單,而這些表單也多半已經被 E 化。我離職的時候也沒寫離職簽呈給主管,只寫了一封 E-mail 。有沒有制度其實只是一種現象,並不代表有制度一定好,沒有制度一定不好,僵化的制度,或不知為何而立的制度,反而是阻礙公司進步的絆腳石。但沒有制度,系統絕對是難做的,因為系統需要穩定、合理、一致的需求,若沒有制度規章當作基礎,就沒有辦法在一個穩固的流程下分析需求,難度絕對比有制度的公司高很多。

總結
前陣子看到 Discovery 講人體的奧妙,他說人體就像車子一樣,車子老了就需要定期更換零件,車子老了,就沒辦法像年輕的時候一樣跑那麼快,但是因為你變得比較有智慧,所以也不需要跑那麼快了。
M 公司就像是一台老車,速度已經沒法很快了,但是由於比較有經驗,因此不需要跑那麼快,也可以抵達終點。 M 公司該做的就是定期的保養,把一些爛掉的零件換一換。
L 公司就像一台新跑車,速度領先全球,速度雖快,但不一定比別人快達到目標,因為經驗不足,可能會多走很多冤枉路,當然開車的人也可能飆的很爽,青春不就是要浪費在美好的事物上嗎?

2009年4月9日 星期四

Why Object-Oriented?

今天在 ERP System Administration 課堂上,
老師問到:「為何要用 OO 」?
有同學回答:「Reuse」。

六年前的我,也是這個答案。一個對於 OO 參透不深的人,通常的答案都是 Reuse 。
記得 2003 年,我初出茅廬,到一家叫做「物件動力」的公司面試,為了面試,當然要惡補一下物件導向,所以稍微瞭解了一下 PIE (Polymorphism, Inheritance, Encapsulation) ,查了一下物件導向的好處,就去面試了,考官當然會問這個問題囉,我的回答也是 Reuse 。

六年後,經過六年的實際體會,我的心得是:「 Deal with changes 」! Reuse 只是 Deal with Changes 時所產生的一個好的副作用,就像有人吃痘痘藥,結果胸部變大一樣。

記得有一年,因教育訓練的關係,我認識一位台灣軟工界傑出的女性:邱郁惠小姐(她的 blog 如下: http://www.umltw.com/ ),她是我們公司聘請過來做 OO 教育訓練的講師,他對「 Why Object-Oriented? 」的看法是:

Process-Oriented 的基礎是流程,Object-Oriented 的基礎是物件,在真實世界中,物件相對比流程來的穩定,因此我們會希望我們的系統能夠 Base on 一個相對穩定的基礎來開發,因此我們選擇物件導向。

我覺得她說得算有道理,至今我還一直相信她的想法,舉例來說:

一個旅館系統來說好了,今天我們要提供一個房客可以自行上網預訂房間的流程,過了一個月以後,我們可能又要提供一個客戶滿意度調查的流程,再過一個月,我們可能又要加一個客戶列入黑名單的流程,半年後,我們發現預定房間的流程有瑕疵,應該在訂房前要先檢查房間數夠不夠,所以又加了一個可用房間檢查流程。

隨著使用者需求的改變,我們的流程也不斷的變更。

做軟體最常用的一招叫做 Divide and Conquer ,把一個大的東西,切割成小的單元 - Software Element ,然後把小的單元一一征服後,軟體就完成了。
如果今天我們切割 Software Element 的方式是 Base on 流程來切割,會發生什麼現象。

1.預訂房間: 1 個 Software Element 。


2.加客戶滿意度調查: 2 個 Software Element


3.加客戶列入黑名單:3 個 Software Element


4.加可用房間檢查:房間預訂流程再切割成兩個子流程。


隨著流程的改變,我們的 Software Element 的切割也跟著改變,系統架構很不穩固。

如果我們 Base on 物件來做切割,而非流程,會呈現什麼情況?

1.預訂房間: 3 個 Software Element 。


2.加客戶滿意度調查: 3 個 Software Element


3.加客戶列入黑名單:3 個 Software Element


4.加可用房間檢查:3 個 Software Element


到目前為止,不管流程怎麼改,系統的架構上都是 3 個 Software Element 。

以穩定的架構來應付多變的流程,我想應該是使用 Object-Oriented 的一個很好的理由之一。而穩定的東西,才能達到比較容易達到 Reuse 。如果今天某個 Software Element 一天到晚改變,你敢 Reuse 它嗎? Reuse 它以後他改變了可是會影響到你喔!
這也是為什麼我認為 Reuse 只是 Deal with Changes 的一個好的副作用的原因。當然物件導向的系統架構也只是比流程導向相對穩定而已,要做得更好,還必須使用很多物件導向延伸出來的方法才行,比如: Design Pattern 。

除了 Deal with Change 外,還有一些理由我覺得不是很充分的有:
  1. 更貼近真實世界的運作方式
  2. 更符合人類思考的方式

這些都是坊間的一些書會提到的,我想這些書都抄來抄去,所以理由也都差不多。但我一直覺得這些理由並不充分。

「更貼近真實世界的運作方式」
那又如何?能帶給我什麼好處嗎?為何我的系統要更貼近真實世界?
而且事實並非如此,就拿旅館預定系統的例子來說好了,如果你要檢查目前房間有沒有空著,在真實世界上誰會負責去做這件事?應該是旅館服務人員,或顧客自己去看,但我們的設計卻是房間自己檢查自己有沒有空著。並不跟真實世界相符阿!
有人會說:貼近真實世界會讓你的系統看起來更直覺,更容易理解,但每個人看世界的方式與角度都不同,我對這個世界的理解跟你對這個世界的理解可能差異很大,因此你用你對這個世界的理解方式寫出來的系統,對我來說可能並不直覺,也不合理。

「更符合人類思考的方式」
有人說(甚至 N 年前我也曾經這樣說過),程式語言進化到 OO 是因為 OO 更貼近人類思考的方式,因為電腦是以程序的方式運作,所以我們早期的程式語言都是為了遷就電腦,所以也用程序的方式來撰寫程式語言,慢慢的為了讓語言更貼近人類思考的方式,所以演化出物件導向語言來。
如果真的是這樣的話,那原本學程序導向語言的人類,接觸 OOP ,應該會如魚得水吧!但真相卻是一些寫 COBAL 的老人要叫他寫 Java 簡直要他的命。

他們的思考方式是這樣:

電腦 = 計算機

計算機 is
[我輸入一些東西]
[經過他的一番運算]
[他會給我一個值]

然後我再輸入一些東西,他會再給我一個值,然後經過一番連續的 input-process-output ,最後得到我想要的一個結果。

這樣的思考方式也沒錯阿,他把電腦軟體當作一個冰冷的工具,他永遠沒法想像軟體,就像一個真實世界的縮影,裡面有個虛擬的客戶,虛擬的房間,此虛擬的客戶會對虛擬的房間做出預訂的動作,然後我們在這個虛擬世界外面的人,透過螢幕看到這虛擬世界所運作的結果。
因此我覺得「更符合人類思考的方式」,只是更符合發明物件導向的人所思考的方式而已,並非全人類。每個人對要完成一件事情的思考方式都不一樣,並非每個人都會幻想出一個虛擬世界來完成他的任務。

到目前為止我的參透,物件導向帶給軟體界的好處只有 Deal with Changes 而已,而且也並非能完全解決 Changes 的問題。