2008年6月10日 星期二

Q18 努力的方向(10)──留下現在的記錄供未來比對

終於,是現在階段的尾聲了。不知道在努力實現想法的同時可以影響多少人往相同的方向努力,也不知道是否存在著可以改變自己想法的現實。在此時留下程式設計的完整想法,總是可以在未來的某個階段再回頭檢視那時與現在的差異有哪些,而不會每次都懷疑著自己以前到底曾經做過些什麼。

如果你也認同這些,是不是也應該開始改變些什麼了?唯有開始動作,才會對未來產生影響……。

2008年6月9日 星期一

Q17 努力的方向(9)──循序漸進的想法

從別人指定的功能需求下找出適當的流程與動作,用計算機程式正確地達成目的是一種快樂,這種成就感正是這個領域吸引人的魅力。

然而在系統範圍較大、需求較多的時候,各個功能之間總有採用相同動作的時候,如何管理這些重疊的地方暫時都得靠個人的想法。強調絕對重用的人會讓重覆動作只存在於一個地方,只注重完成功能者就可能在各功能裡都有一份相同的動作程式碼。這樣的重疊也會出現在下一個相同領域的專案上,而且除了功能實現的想法外還要加上與前一系統的異同分析;甚至不同領域間的系統也要相互分析比較。不想作這些比對的人,手上就只會擁有一堆無法相容的程式碼。

歸本溯源,最初的想法都只是“如何完成一個功能”,想法是沿著Use Case的流程行走。不過這個功能的分解動作為了提供給同系統內其他功能使用會被收集到集合裡,整理出每個集合提供什麼動作也是必要的工作。對集合而言,每個提供外界操作的動作都是它的功能需求,如何在內部達成這個需求又回溯到“如何完成一個功能”的想法。

“想法”是所有人完成功能的思維,不管它被放在文件裡或是程式裡都是確實存在的。找出別人曾經有的想法與留存自己現在的想法,都是讓設計經驗得以留存的重要關鍵;只是現在大家普遍使用的記錄方式都無法保證足以讓他人明瞭原有的想法。因此,改善現有的作法以留下現有想法以減少後來負責人的負擔正是需要努力的方向。

要能從程式碼中快速回溯出原有的設計想法,需要的兩個前提是:正確的Component結構與Java構文解析。

2008年6月8日 星期日

Q16 努力的方向(8)──不同Domain使用的SA Tool

在相同Domain裡開發過幾個專案後,應大致可以掌握數個專案間必須要有的相同項目與其間的差異。SA Tool的功用就在於快速地從經驗裡選擇需要的功能,並能因應客戶的個別需要客製化功能細節(動作的變化與使用的資料等),快速地把設定與產出匯出給之前累積的系統程式來執行。

SA Tool的使用想法大致是:首先依類別顯示所有的功能清單,使用者勾選系統應包含的功能進入待開發清單;每個功能列出達成目標的分解動作(同時顯示對應的API)予以挑選與組合,必要動作需標註不得省略;每個動作使用的必要資料名稱也會在選取動作的同時帶出來。功能、動作與資料便是系統的精髓。

工具也必須至少分成數個層次,從最底層的單獨元件的選擇開始、組成架構的設定、管理工具的選擇(顯示對應的DB Schema)、Domain基本功能(顯示對應的DB Schema)、基於選擇的基本功能而出現的功能選項等等。依照重覆使用的層次出現各個層次可以選擇與設定的項目是工具進階的要求。

系統分析人員在一邊選擇與設定的同時,必須完整記錄有哪些欠缺的功能與無法做到的設定,這些是在這個專案開發的同時必須完成的工作項目。等到系統的經驗累積到用選擇與設定就足以完成系統功能時,就表示在這個Domain下已經擁有成熟產品。

2008年6月7日 星期六

Q15 努力的方向(7)──將經驗帶到往後的同型專案

如果使用能夠記錄下開發歷程的方法來設計系統,理應從第二個專案開始起可以使用到前面專案累積的經驗。其實只要開發過一個系統肯定會有經驗的累積,一個開發團隊的評量除了達成客戶要求的功能之外,就看他們把經驗累積到哪些地方。

最不好的經驗傳承是存放在自己的腦袋裡只留下程式碼,因為未來可以說是什麼都看不懂;再好些的經驗傳承是使用文件,很多東西都可以查得到,不過得看程式與文件有多少的同步化;較好的經驗傳承就是從程式碼裡產生完全正確的文件與想法,然而這也得視程式元件的定義是否理想。

在切割好所有Use Case、Module與Layer的層次並擺放好所有的對應元件後,將專案裡的Use Case逐一依設計的方法與使用的物件,逐層地放置到方法與資料應該在的元件並封裝對應的邏輯。下一個專案開始時將所有的產出先帶過去,再逐一檢視Use Case、方法與資料有哪些可重用、有哪些需要修改與哪些需要新增。

以原有的清單作為基準再記錄下異動的種類,每個差異都是新專案裡的工作項目。記錄下前後差異的項目是系統分析者的責任,研擬出融合新需求又能再拿回前一個專案的產出則是程式設計者的挑戰。如果經驗無法往後重用,那麼每個專案都幾近重新製作且各自平行,這又有什麼意義呢?

2008年6月6日 星期五

Q14 努力的方向(6)──制訂專案開發流程與產出

功能就像是待達成的目標,而設計就是找出完成目標的方法。對於單一目標來看,我們可以不用管層次或是共用問題只要達到目的就好;但是對於有許多不同功能存在的系統來說,進行Module層次的方法分析以抽取相同方法的動作卻是不應省略的。

在專案上的作法會從Use Case開始,先行分析一些重要的功能,檢查在完成功能時所需要的各個動作,並將之安置在對應的Module裡。接著逐步分析所有的Use Case,取得動作的最大集合並放置到對應的Module。在此之後應可以得到所有的系統動作與全部的Module,還有這兩者與Use Case的關聯。

動作的拆解比較趨近人類日常生活的想法,不過每個動作所操作的資料則是Domain特有的資訊;一個系統裡所有動作操作到的資料集合,可說是該系統Domain的範圍。每個動作用什麼樣的方式來操作資料,每種資料總共提供了哪些操作方式,在專案上主要是以使用者角度定義動作與資料的名稱(再往下是從設計的角度來定義動作與資料實際的程式分類)。

將系統的定義資料與操作方法分門別類地定義並明確地標示,在使用時快速地取得全部索引從中選用(同時藉此保證同一目的的程式碼不會存在多份),這是我所認定可以加快專案產能的方式。

2008年6月5日 星期四

Q13 努力的方向(5)──Source Code的佈署與混淆

Component的內部依其意義切分為數個部分是令人容易理解的結構,Interface-Abstract Class-Class的佈置則有助於重覆使用的實現。但是數量較多的程式碼與執行時大量參數的處理是否會造成執行效能的低落?答案當然是肯定的。

為了兼顧設計時的意義明顯與執行時的效能提升,我的想法是依據Properties裡的定義匯出執行時所需要的Java Source Code與設定檔。匯出是把Component完整結構裡的十八個檔案(扣除Component Interface)裡的變數與方法全部組成到一個Class裡,這就是現今一般很難令人看懂的程式編排,但是與完整結構比較起來卻有較佳的速度;設定檔則逐一判斷,儘量執行時用不到的屬性在匯出時變成程式並將之移除,使設定檔內只有必須的部分。

在匯出的同時還可以作反組譯的保護。定義隨機選擇一些英文字母作為方法的機制,在組合各個部分的方法時順便變換變數與方法的名稱,藉由把所有的方法集合成一堆同時讓名稱的可讀性大幅降低,就可以嚴重妨礙想藉反組譯來看懂程式的意圖;而且在此同時,我們手上擁有的還是結構清楚的原始程式。

2008年6月4日 星期三

Q12 努力的方向(4)──自動產生設計文件

設計文件一向都是程式設計者的最痛,除了要讓寫出的程式正常運作之外,還要把設計的理念訴諸文字。在寫完文件後倘使遇到還需要變動程式(或重構)的狀況,之前產出的文件要不是重新翻寫就是無人聞問。如果,設計文件可以跟著程式自動產生,是不是很棒的夢想?

第一份文件是Component Interface說明,每個Method的用法與說明使用都以Java Doc的方式寫成註解即可。第二份是每個方法實作的流程圖,流程寫在Flow的方法裡而動作寫在Action的方法裡,運用程式解析的工具很輕易地可以得知每個方法的流程與動作;目前至少可以抓出流程的註解與動作的Java Doc來組成步驟的順序,未來可以搭配流程圖API完成自動產生流程圖的理想。

第三份文件是每個流程與動作對於內部變數以及的Data Model、Properties與Exception使用關聯,在每個流程步驟或是動作裡,所使用到的變數、資料、參數與例外處理(不管是常數或是方法)都能夠在逐步演算程式的同時找到,只需要將各個關聯記錄下來,很容易再使用JXL產出所有的關聯追溯表。

第四份文件是每個流程與動作對於其他Package Interface Method的使用關聯,同樣可以在產生第二份文件時在每個流程步驟或是動作裡找到;只需要在處理時記錄就可以直接產出,甚至連只是單純使用或是內部有宣告變數放置都可以判別。

只要讓所有Package都具有相同的組成與意義,我相信依循這個想法所發展的設計文件產生工具並不是遙不可及的夢想;若有幸實現,這將是許多程式設計人員的幸福。

2008年6月3日 星期二

Q11 努力的方向(3)──規定元件內部軟體結構

每個Package的結構都會以自己經驗累積的一個Interface加六個Class為主。Interface是提供給外部操作的功能規格,也就是這個Package所應該實現的所有功能;六個Class則是以開發元件時切割的部分為基準。

在今年進行的專案裡,我已經準備好基本型態的Component,提供最根本的實作。另外還準備了一個小工具,只需要輸入Package Name、Component Name、Super Component Package Name、Super Component Name並選取要產生哪些部分,就可以自動產生所有已經命名好且建立好繼承與實作關係的所有檔案。

Package內部的適當切割有助於讓不同的人快速地看懂程式,這是因為各種相關的東西都集中在對應的部分,對外所使用的都是其他Package Interface。Component的首要是定義功能的Interface,對需要分層負責的各個部份都宣告Interface;Component內部則以Implementation作為入口,再分散到各個部分內部去處理。

雖然Component只需注重Interface的實現,內部怎麼去完成的可以不用在意,但是所有的Component都維持同樣的生成方式、同樣的分工模式與想法時,除了作法可以統一外,還可以根據一致的風格完成更進一步的目標與對應工具。(Component產生工具便是一例)

2008年6月2日 星期一

Q10 努力的方向(2)──建立軟體元件庫與使用文件

建立可以切割並重用的系統後,接下來是讓所有人都知道每一個空格裡有哪些元件、各有什麼用途、提供了哪些方法等等,這相當於建立全部元件的資料庫。建立的主要目的在於讓所有人可以快速知道全部有哪些元件、元件被放置在哪一個空格,當然也應該可以依特定目的來尋找。

下圖是一個專案裡的部分元件,所有的元件都依應用的層次被放置到對應的定義層次裡。如果有一個專案只是需要基本的架構在上面開發,就只需要拿基本與架構用的所有元件;如果要特定系統的元件就再多拿一層系統用的元件。除了基本架構的快速組成,在各個專案遇到的元件問題在解決後可以適用到其他使用同樣元件的專案裡。

分層負責的意義在於要做的動作在每個地方、每個層次都有負責的元件。如果開發的人為了求快速完成功能,而沒有去定義綠色圈圈所定義的元件時,我們可以思考一下原來該在那裡的變數與程式都會跑到哪裡去?找出那些消失的東西與學會使用它們是否需要額外的時間?是不是每個人都要去懂?需要使用部分的層次時,那些缺少的東西掉落在哪裡?

個人的快速是由於減少封裝的元件,進而造成其他人使用上的困難是我所不能接受的作法,更何況那些問題到頭來還是會掉在自己身上。維持元件架構的完整與提供齊全的文件都是在開發的同時應該做到的責任。

2008年6月1日 星期日

Q09 努力的方向(1)──定義各階層的應有元件

在作程式設計時,實在是有太多人只以最表層的執行結果作為目標,全然沒想過抽取相同子流程、避免相同程式碼與動作封裝到適當元件的方向。以這種方向作出的系統,在較公用的方法裡通常堆滿了不知從哪裡來、為什麼而做的程式,到最後只能使用一堆if-else來避免修改時影響到其他部分;同時千辛萬苦堆積好的系統,如果下個專案需要的只是基本框架的時候,根本沒法快速地把上層的部分移除乾淨,造成明明要用系統的一部分卻無法做到。

完成目標首先要切割出逐步進行的分解動作,接著把動作定位到系統三度空間的確定位置,最後把該空格內的類似目的的動作各自封裝到元件裡(分層負責);經由這樣的設計將系統的經驗收集在可以重覆使用的元件,使用時只應該找到唯一一個可以使用的動作方法(絕對重用)。元件提供給上一層次的是固定的動作,上一層次要如何使用這些動作則需另外再定義一組流程來達成目標(動靜分離);如果與實際需求有差距,就需要找另一個實作同一介面的元件或是另外定義一個作法來滿足需求(靈活替換)。

在下面的系統組成方塊圖裡,從專案Use Case的起點開始,找出達成需要的各個Module,每個Module的方法再往下串到不同層次上的元件動作,這一切的集合正是完成這個Use Case的所需的全部。為了避免多個動作被壓平為一個方法而形成無法抽離的困境,會努力將系統設計為不管從哪個方向切開都能夠再度使用(在切開的同時也要能看出切開部分所有的關聯)。