顯示具有 [I] 設計閒談 標籤的文章。 顯示所有文章
顯示具有 [I] 設計閒談 標籤的文章。 顯示所有文章

2007年12月31日 星期一

I15 紙上談兵誰都會,真槍實彈的話呢?

“修改程式後應該通過所有相關的測試,才能保證程式是正確的。”我問過認識的所有軟體工程師,每個人都同意這個說法。再進一步問:“那麼修改程式後要如何取得正確範圍的相關測試呢?”,至今還沒有聽到有人提出理想的解答。(註)

這就是理論與實作的差距。很多聽起來很美好的理論,像是軟體外包會減少專案支出,軟體工廠的元件化能重覆使用程式可以降低成本,這些都因為沒有可以做到的方法而使得講求實際的專案根本無法導入。理論與實作在大家的口中都是無法串接在一起的平行線,軟體工程的理論變成討論時抬槓的內容而已。

在撰寫Blog後兩三個月內,心裡理論串接到實作的關節差不多都打通。在2007/11/12凌晨為了思考如何在專案裡套用軟工的作法,前前後後想過各個細節的作法、作用與影響;確認一切都能依序做出後,又再思量要如何向主管證明這樣做的好處以及施行的內容等等,到最後幾乎整晚都沒有睡好。之後的幾天,以心裡的想法與同事、主管們討論過實施的細節,慢慢地建立起每一個專案產出物件的關聯,同時套用設計工具並實作出一些簡單的開發工具來加速開發。

這真的是一條很長的路,在很多地方都需要認真思索該怎麼做、如何做才比較好,也難怪絕大多數程式設計者只想省事地做出符合功能的系統而不願花精力走這條路。但是,我以近兩個月內以這個模式迅速建構起兩個結構穩定系統的實作經驗告訴各位,邁向理想設計的路途雖然很難走,但是走過之後就絕對不會再用以前的方式做事……。

註:同事告知有xUnit Test可以作出涵蓋專案裡所有Use Case的測試,刻意在修改的程式裡傳回錯誤的值就可以立即發現哪些Use Case沒有通過而得知這段程式影響了哪些Use Case;不過要寫出好的xUnit Test內容投入的資源可能與開發這個專案相去不遠。而我自己的解法是先知道程式所在的method與使用它的所有Interface Method,然後去查詢所有使用到那些Interface Method的Sequence Diagram,所屬的Use Case Realization即是答案;當然,要記錄下所有的關聯也得花不少功夫。一切就看自己喜歡哪一種方式。

2007年12月30日 星期日

I14 進化到功能流程定義更難

想要再進化成像SOA那樣理想化,只要畫流程圖就能做出功能的境界,除了元件庫裡的元件要跟得上需求之外,還必須寫出更複雜的執行架構。

再往上包裝為功能流程定義來應付使用者不同的需求很困難,因為使用者想做的動作種類太多,我們必須先推測有哪些地方會有改變,而且還要預測使用者”可能”會怎麼改變。這些預測的動作都要先準備好對應的元件,才能夠讓使用者不要寫程式而只需要設定就可以達成。所以快速新增動作的元件庫是必要的。

執行架構與元件庫都準備妥當後,製作一個繪製流程圖的工具並提供所有元件讓使用者選用,在流程製作時指定已經具有完整功能的元件或API,如果一來任何一個人在客戶端談好需求並拉好流程圖設定,不用寫任何一個程式就可以立即完成一個功能。另外,要有將部分動作提取成子流程再呼叫的機制;將流程也依架構設計的原則分層負責自己該做的事,將可以組織成架構分明的系統。

這兩年在工作上遇到印度最大的Ta Ta公司到台灣作生意,在作法上他們就採用類以這樣的方法來開發系統。雖然現今在特定領域裡的元件與彈性都還不夠理想,但我很訝異其他國家已經進化到這個地步了;我也相信再過一段時日等他們的元件庫壯大後,所有用程式做功能的公司將都不是敵手。

我深信,能夠把系統開發進化成功能流程定義的公司,將會是最後的贏家!

2007年12月29日 星期六

I13 用程式包裝執行架構難

為了解決程式的僵化問題,把功能流程切分為需求層級與動作層級是必須的。動作層級是包裝在元件裡通用的,注重動作的正確性與所有錯誤狀態的回報;需求層級則是依使用者需求隨時客製化,保持變動的部分。元件強調的是動作的一致性以便重覆使用,節省開發的成本。

需求層級的部分最低要求是直接寫程式達成,但近年來所流行的系統架構則慢慢把一部分需求的製作帶到用設定檔的方式。如果系統架構是自己開發的,那麼需要設計出設定工具與執行環境;對這樣的一個架構需要定義出適用的範圍與設定的方式,負責開發的人就不是只應付一個功能,而是要應付在定義範圍內使用者所有可能的需求。這樣的設計難度已經提高很多了。

有些設定僅是設定實作的Class是哪一個,執行時只要叫起指定的Class並使用就能運作,不過在直接做時只需要一行new的指令,到架構設計時需要讀入設定檔再使用Factory Pattern來產生實體以執行的方法,相對已經變得複雜;再加上有些設定檔必須有流程的控制,對於能力較不足的人將會是個挑戰。

與用程式寫功能比較,若能包裝成執行架構就能有較多的地方不需要寫程式,如此可讓一般不會寫程式的使用者來參與系統的開發而且可以加速功能的達成,不過指定執行Class的實作還是需要會寫程式的人來做。

2007年12月28日 星期五

I12 用程式實現特定功能易

回想起2001年第一次用Java寫出公司需要的系統時,功能全部都符合需求,但是被評論為裡面寫法跟C語言差不了多少。那時連SCJP都沒通過,觀念不清楚的情形下寫得亂七八糟是可以預期的;但這也表示即使是亂寫,也是可以寫出堪用的系統。

用程式實作已知的功能比較簡單,因為功能與動作都是固定的,只要明白功能想做的事就有法子“組裝”出想要的模樣;即使寫錯也沒有大礙,頂多砍掉重寫這一塊便是。在求功能正常與開發快速而不注意影響與變更的前提,可以說任何一個人都能把系統做出來。

只是以這種思維去開發系統,得到的程式碼很可能品質參差不齊、沒有經過適當的測試同時也沒有記錄使用上的追溯。在程式包含太多功能的邏輯又分布在與功能相關的Class中,這表示該元件已經被特殊化,很難適用在其他只有些微差異的類似需求上。此時Copy-Paste就會出現,生成另外一組程式碼再直接修改內容去套用新需求。空有一大堆程式碼,卻沒法把相關的需求封裝在同一組程式碼裡。

僅注重眼前功能的實現,是造成系統程式僵化的最大原因。

2007年12月27日 星期四

I11 架構僅是外殼,設計方為本質

最近工作上開始接觸J2EE與SOA,發現到他們都是架構上的延伸使用,最重要的本質還是在Interface的訂定與內部的設計。

J2EE裡有三種EJB:Entity Bean、Sessiong Bean與Message-Driven Bean。以本質來說,Entity Bean相當於Data Model、Sesstion Bean等同於Controller、Message-Driven Bean則類似Event Listener。定義出Interface與實作Class後以EJB的型勢包裝起來就適用在J2EE的環境裡。

SOA也很類似,先準備好Web Service元件;其內部可以再使用其他Web Service元件、EJB或POJO做成的元件,重點同樣在於定義的Interface與實作Class。比較特別的是,在組成客戶功能的時候是使用製作流程圖的方式,拉進元件後判斷執行的結果來決定下一步要走哪一條路徑,在元件備齊的狀況下不需要再寫任何程式碼就能組合出符合客戶需求的功能。所依賴的是整合性產品解析一堆描述資料來“自動產生”功能裡的動作。

雖然應用的範圍如此廣泛,但可以解析其內部作法:首先是做出帶有Interface的元件,接著可以往上包裝成EJB;以上兩者都可以再包裝成Web Service。使用上的技術規格是需要去學習的,但是Component的設計本質則是我目前著重的方向。

2007年12月26日 星期三

I10 Iteration開發方式原則

要在專案裡使用Iteration,必須先對系統有初步的架構設計才行。因為我們需要知道功能之間的使用關係,把下層被使用的功能排在前面,上層使用下層的那些功能排在後面,再上層的功能則排在更後面。

以設計系統的順序來看,硬體架構與其相關的橋樑功能必須在第一個Iteration裡,因為所有的功能都是架構在這之上。(可以想像一下其他功能先做時,到最後沒法子完整測通時的窘態)其次就是子系統的畫分與排出優先順序,當然得要參考子系統間的使用關聯與客戶想法。

在子系統裡第一個要做的絕對是Data Model,資料模組沒有定義好還真不知道程式要如何生出來。接著就條列出子系統中所有的功能,一樣是參考功能的使用關聯與客戶想要先做的來安排。最後把排列好的架構、子系統、Data Model、功能放到一張表裡略估工作人天,專案經理參考系統區塊與工作時數並依其智慧由上到下框出每一個Iteration的範圍,再加總Iteration裡的工作時數安排時程。

千萬要遵循的原則是:放在底層的功能一定要先做好!否則等到系統快完工時,最後才做的架構發現有問題而必須更換成變動較大的方案時,我敢保證這個專案一結束就會樹倒猢猻散。

2007年12月25日 星期二

I09 如何學好英文

說起來,語言的使用也與程式設計扯上很大的關係,因為語言的使用同樣可以分為文法(Controller)與單字(Action),適用程式設計的思維。

首先我們可以把文法的句型視作為Controller,每一種句型都可以對應到一個想表達的想法(功能需求)。所以心裡在有某種念頭時,要能夠立即選用出對應的文法句型來;這個前提是心裡必須要存放所有文法句型以供需要時挑選。

再來是單字(Action)的挑選,單字的詞性可視作為Interface,整個句型的組成就變為只要挑選同樣詞性且符合心裡意義的單字即可。如此逐一把挑選的單字放入句型,完成後迅速在心裡從頭到尾檢查(測試)一遍,改掉有問題的單字並重新檢查直到沒有問題後就表達出來給對方知道。挑選單字時,當然心裡要有足夠的單字庫可查詢,否則你能表達的英文永遠只有那幾個字,所以背誦單字且明白詞性是一定要下的苦功。

要用英文表達時也切忌先用中文造句後再轉換為英文,應該在心裡組成“句子的意思”後直接成為英文;因為先組成中文於再轉換為英文,會造成先有中文變為意思再對應成英文的轉換時間差,同時會有使用英文卻得連中文都一起綁住,使得句型與句意的使用受到另一種語言的影響。想法直接組成英文句子是最理想的方式。

註:師長與同事一致認為我的英文實在不怎麼樣,如有人受本文影響改變學習英文的方式卻未見進步,本人恕不負責!

2007年12月24日 星期一

I08 廚師改行設計程式應是好主意

廚師是利用各式各樣的食材組成佳餚的重要角色,一名出色的廚師必須先知道要做的菜是什麼,還要明白每道食材的意義與可能造成的影響,才能選擇出最適當的食材並參考其影響來制訂出最理想的步驟以完成該道美食。

現在暫時把要做的菜視為需求、把烹煮的流程控管視為Controller、把烹煮的步驟視為Action、食材視為Component,我們可以改寫第一段的最後一個句子:一名出色的廚師必須先知道要做的菜(功能需求)是什麼,還要明白每道食材(Component)的意義與可能造成的影響,才能選擇出最適當的食材(Component)並參考其影響來制訂出最理想的步驟(Controller & Action)以完成該道美食(功能需求)。

在調理的時候,廚師還要視臨場狀況來決定是否增減某種食材或調味料,或者些微調整作法等等,就等同於系統設計時的狀況判斷與例外處理。由以上的觀察可以推知廚師除了重視需求的達成外,還會注意到使用元件的效果與影響,進而研擬出正確的進行步驟與方法;相信對具有程式設計思維的廚師加以基礎的訓練後,可以適用於開發系統的工作。反之,程式設計師也適合去當廚師。

雖然有位同事同樣具有烹調美食的能力,但是如果有人在專案缺人時找一群廚師加以訓練後就開始開發,我絕對不會擔負任何責任的;反之,如果聚會時直接把食譜與食材丟給程式設計師,最後搞到大家沒東西可吃時,我也同樣不會擔負任何責任喔。

2007年12月23日 星期日

I07 設計常見問題(5)──問題並不在方法論

以上提到的幾個問題時常發生在程式設計領域裡,幾乎就是系統開發失去彈性的主要原因。

問題的成因是因為設計人員對於客戶的需求只在心裡產生對應的模型(系統做到剛好滿足需求即可),只注重達成現在的功能而忽略了物件與動作的關聯及責任,以致於在客戶提出一個踩到痛處的小變更時,整個團隊改到天昏地暗。

我刻意對方法論隻字未提,因為心裡的設計層次正確時,不管用哪一種方法論來開發都能夠保有彈性。相對地,如果思考的模型只為了做出來,那麼不管用哪一種方法論,你的專案同樣會一直修改,直到不成人形再棄置一旁,然後每次的下一個專案又另起爐灶重新來過。

雖然設計的成果與方法論無關,但是OOAD的思維談的就是物件的佈置、責任與關聯,這是XP的4項價值觀和12條實踐法則裡完全沒有強調的東西。這兩年來自己在程式設計方面的成長,要歸因於接觸OOAD後有所感觸並努力思考與實踐所致。

2007年12月22日 星期六

I06 設計常見問題(4)──用重構來解決問題

“重構是解決程式裡所嗅出的壞氣味”。我是不大明白理想的重構應該針對什麼樣的範圍做什麼樣的事,但是“現在隨便寫寫能動就好,反正未來還會再重構的“,卻是不少系統設計者拿來作為沒有花時間做良好設計的理由。

在我的理念裡系統是一層一層靜態物件搭配動態方法所組合起來的。從最底部的硬體架構搭上溝通橋樑、在上頭佈署元件結構再搭配定義介面、再向上有程式類別與方法呼叫,甚至連傳送的資料也有層次的不同;每個層次又有不同的參數、反應與控制動作。整個系統就像是用積木堆疊起來的巨大模型。

重構如果能侷限在局部不影響其他任何模組應該是最理想的結果,但是我所看過的重構卻是動不動就產生一個新的類別與介面,或是更改物件繼承與實作的關係。架構與物件間層次的變動會影響構築在其上的所有程式,物件繼承特性的改變也會影響內部的行為與使用的時機;這樣的改變將會造成許多與之相關的程式變得非常不穩定。

如需上述的重構應該在上頭還沒有程式構築其上時儘速進行,等到系統都快完成了才發現要變動架構或物件特性,就像民眾都把房子蓋在某片土地上後政府才說那土地是要蓋公園的,或者是像結婚典禮快開始了才發現配偶的性別不是原來我們所想的。有些改變太晚發生時,造成的影響實在是太深遠了。

2007年12月21日 星期五

I05 設計常見問題(3)──邏輯散落多個層次

在D02裡提到Use Case裡的邏輯應被拆分為需求邏輯與動作邏輯。需求邏輯是根據客戶的規格加以規劃應有的動作與順序,動作邏輯則是與需求無關而是以能夠完成指定動作為目的;後者的作法應是固定不動,動作的微調用設定與參數來改變,前者需去適應客戶可能有的改變,再依動作的規格找出適用的元件或專案上的特定方法。

需求邏輯與動作邏輯的分工可以讓重覆使用的元件與專案的客製化差異能同時存在並達到相輔相成的效果,其中的使用界限是非常明確的。但是一般在設計時很有可能發生需求邏輯散落在功能邏輯裡做事的問題。

客戶要在進行列印動作後在畫面顯示一個對話盒告知成功或失敗原因,在我的設計觀念裡訊息的內容與顯示方法都該在需求邏輯裡處理的,不過專案裡如果沒有人去定義層次與該在哪一層做,訊息的部分有時會不小心被寫進印表機的動作程式裡。這便是所維護產品裡的一個實例,而且兩組印表機的不同實作竟然是一組沒處理訊息而另一組有。

在訊息處理寫入印表機動作的那組實作,在不同專案裡想把訊息顯示在畫面欄位,此時必須加一個顯示方法把欄位傳進去才能在裡面顯示。但是這樣一來印表機程式就綁上了顯示的元件,要不然就是重新改寫印表機實作;改寫比較符合設計原則,但是改了以後之前使用訊息處理內建的那些專案要怎麼辦呢?

專案裡時常發生東西改來改去的狀況,而且一改就是一大片,其實那一大片都是自己造成的……。

2007年12月20日 星期四

I04 設計常見問題(2)──動作責任定義錯誤

在系統裡兩個物件之間的互動動作,有的時候並不能很明顯地判知該動作要由哪一個Class提供動作方法;不管是客戶提供的需求模糊或是設計者的觀念不佳,都有可能使得動作的責任定義在錯誤的一邊,造成物件的層次被打亂而難以應付需求的改變。

比如說人與車的關係,人可以駕駛車輛,所以有Person.drive(Car)的方法,在裡面再執行Car.move()讓車去計算移動的內容。如果設計的人一時不察,把車子移動的計算寫在drive()裡再直接設定Car的相關資訊的話,雖然呼叫Person.drive(Car)這個功能同樣可運作,但是往後把Car移到其他的系統時就會發現Car根本不會move()。

動作責任的錯置在功能測試上很難測試到,即使是Unit Test裡如果測試程式由設計者寫的話,那麼他將會用自己的定義來作Unit Test而沒測到。問題將會直到某個情境下,Car被獨立出來後才會被發現,但是到那時錯誤的drive()方法已經不知散落在多少地方而難以修正。

動作的責任定義錯誤或是內容遺漏都是負責設計的人在依劇本切割時,必須戰戰競競地時時妥善加以定義。唯有所有Class都僅守本分才能讓系統更有彈性去改變。

2007年12月19日 星期三

I03 設計常見問題(1)──物件範圍沒有對應

有一種心理測驗是這樣的:在紙張印上一團不規則的黑色區塊,要人說出自己連想到的第一種物品,藉著幾種可能的答案來推測受試者心裡的想法。在設計Data Model時也有點像這樣;每個人都知道要根據Domain設計出系統專用的Data Model,可是同樣是專案的成員設計出來卻風格迥異。

舉個生活上的實例來看。客戶描述他們需要的是“椅子”,藉由椅子可以提供他們靠著坐或可以站在上面拿高處的東西;需求講得很單純,於是A廠商想的椅子是一體成型的木椅(一個Class),B廠商想的是有滾輪與腳座的辦公椅(一個Class內含三個Class)。

當然兩種椅子都能符合客戶“目前的需求”,不過客戶可能視臨時的需要而改變需求的內容;例如:客戶希望椅子是不能推動的,而且椅面距離地面的高度增加五公分等等。我們常去抱怨客戶亂改需求,但我們必須知道客戶大多是根據實際需要來改變小地方的;當然,要求改成按摩椅的類型真的就太過份,因為設計理念已經完全不同,怎麼改都會很淒慘。

回頭看合理的兩個小變更,A廠商的椅子本來就沒有輪子所以不用改(同時也慶幸不是改成加上輪子,不然真不能看),但是高度少的五公分該怎麼辦?要換掉四隻椅腳再釘回去好,還是在椅腳下加釘五公分木條好?要是長度再改變的話該怎麼辦?相對起來廠商B就好辦多了,要不要輪子,要多高的腳座都可以拆卸下來換成符合需求的物件;不過在需求沒改變時就相對地多花了不少功夫。

設計是為了應付未來可能的改變,所以我選擇的是廠商B的作法,儘可能地把一個物件拆成多個可抽換的組件,以期望符合更大規模的變更。使用廠商A的作法雖然比較快,但是一旦面對臨時的改變就會顯現出規格訂死的痛苦。

2007年12月18日 星期二

I02 eXtreme Programming的潛在問題

XP的開發方法論,由於強調是輕快與應變,註解只要寫在程式裡即可,所以受到很多人的推崇。XP強調4項價值觀和12條實踐法則,著重在功能設計與測試,並經由不斷地改變與整合保持系統的正確運作。不過支持者是因為這樣做不用寫文件而開發較快,或者是真正遵循它的設計理念而採用就有待商榷。我所看到較大的潛在問題點有以下幾個,也因此不偏好用XP開發系統。

設計者的功力會決定系統的架構。設計層次概念不夠好的人想到什麼就做什麼,極易造成結構的混亂。雖然可以透過回饋與重構來調整,不過架構先天不良時的重構,與我們現在亂寫一通再調整似乎沒什麼差別。

程式中留下註解的質與量是否足夠讓別人看懂?沒有文件的狀況下,所有說明資訊只能依靠口授與註解。嘴巴說的大家可能很快就忘記,寫下來的是不是別人想要知道的呢?

重構時提取的共通部分,是否確保定義了動態抽換的介面?很多時候為了求快,使用物件時就直接用了,等到後來發現那裡應該要做成可擴充的介面時,又得動一大堆程式。而且程式只要一動就有可能出現Side Effect。

測試要怎麼測才算完整?測試的內容根據的是什麼?測試若太含糊勢必會留下很多錯誤,每次出錯就得修改再測;要是測試的內容定義的漏洞很多,將又可能是一個縫縫補補的大漏洞。

2007年12月17日 星期一

I01 OOAD設計基本原則

物件導向設計(Object Oriented Analysis and Design)的精神在於將生活中的實際物件對應成系統裡的程式物件。簡單地說一下我自己使用的設計原則:

靜態物件(Data Model)的拆解原理如下:
有形的物件以一對一的原則生成Class
物件內能拆解出來的獨立物件也要有對應的Class
生成物件Class的同時為之定義一個專用的介面(提供置換用)
符合同樣介面的其他物件Class表示可以取代現在物件的位置

輸出物件(View)的拆解原理如下:
頁面、區域、欄位等等可視物件與可拆解物件以一對一的原則生成Class
生成輸出Class的同時為之定義一個專用的介面(提供置換用)
符合同樣介面的其他輸出Class表示可以取代現在物件的位置

除了靜態的物件,動態的動作(Controller)也需要拆解:
功能執行的流程邏輯由一個隱喻的Class來控制進行
每一個獨立動作視動作範圍拆解為一個方法或是另一個Class
生成動作Class的同時為之定義一個專用的介面(提供置換用)
符合同樣介面的其他物件Class表示可以取代現在物件的位置而存在

如此一來,一個功能需求實作的基本Class組成與關係大致成形。