2009年6月30日 星期二

W26 一名家教班裡的小三學生

不久之前造訪一位朋友所開的國小家教班。在裡面遇到一位小三女學生,她的基礎非常不好,面對簡單的加減應用問題時,是用猜的來決定要用加號或是減號。我試著用數線的方向性來對應加減,也試過讓她固定用大的減去小的來得到答案,但是沒有真實理解數的概念時,一切的說法都沒法讓她解出任何一個類似的應用問題。

周遭遇到的小朋友都還算聰明,很訝異還是有小朋友的理解力如此不佳,這時不禁連想到李家同先生的文章與他所堅持的教育理念。會去留意其他人的的退休計劃,有不少人認為勞碌了大半輩子後,剩下的時光想要過輕鬆的生活或是享受異地的旅遊。

對妻說過,退休後想每年都住在一個不同的偏遠地方,一方面體驗不同環境中生活差異,另一方面就是主動地為附近的小孩進行課業輔導並傳授做人做事的方法。教育的平均有兩個方向可以努力,一個是提升偏遠地區的教育資源,另一個是提升資質較差的小孩到平均的水準,每年旅居到一個不同的區域協助當時所能遇到的所有人,這應該是能力範圍內所能做到的。

資質不好的人因為聽不懂而無法改變自己到能理解的程度,但是聰明的人反倒因為聽懂而更無法改變到更進一層(為他人服務)的程度。缺乏"吸收資訊,判斷對錯,調整自己,成就他人"的成長彈性,到底只能讓自己的聰明才智僅讓自己獲得利益,同時還認為其他人的改變也只是為了他自己而已。(奇怪的是,反倒是心裡有不好的想法時大半會說是因為別的某某事件被連帶影響的)

今天的我對於系統開發與人生想法是如此。明天呢?後天呢?想法還會有再度改變的那一天嗎?

2009年6月29日 星期一

W25 My Object = Data + Behavior

在與兩位同事討論時,其中一位同事說,每個行為包裝為一個方法的想法很像Behavior Driven Design(後來查過資料,發現應更像先不看架構的責任導向設計--Responsibility Driven Design)。Object一直都是包含資料與行為,既然具有這兩類元素,Data Driven Design與Behavior Driven Design就應該同時注重。

角色、物件、大小事都對應到各自的Object(或是元件)並依其特性抽取共用是我的設計基礎,資料結構與處理步驟全採用一對一的對應設計是我的堅持,符合原始特性的定義才能夠將改變局部化到最小區塊,進而得到最少化的測試範圍。這些好處的相對代價是開發人員要額外作設計,必須花費較多的時間為自己與別人安排程式碼。

對於OOAD的題目,我一個人思考了兩年,提出的經驗與作法盡可能涵蓋到工作中所經歷到的各個角落。有一位喜歡看書的理論型同事是我經常討論的伙伴,從他那裡聽說到許多不用功就不會知道的名詞與作法;共事的同事們是我主要的經驗來源,由於他們現有的產出我才能明白各個角落的好處與缺失;專案上的同事則是我琢磨想法的對象,常問他們現在的作法與優缺點是什麼,同時提出我的作法詢問是否能留下好處並改善弱點。

每個地方的說法我都推論與驗證過,雖然能有可以解決的方案,但是我相信並不會是最佳的作法。許多同好都投注許多心力鑽研軟體工程的各個方面,沒有道理我只用兩個人年就有最後的結論。目前差不多是我的極限了,未來除了製作對應的工具之外,更需要的是有其他相信這一條路的人一同討論每個角落的最佳化。

這十年來在工作上所遇到能夠抱持相近努力方向的人,實在是屈一手之指可數呀。(當然不包含發現專案有問題時才喊著要好好設計口號、作下一個專案時又開始計較時程與產出的那些人)

2009年6月27日 星期六

W24 成敗的關鍵(2)──看待的角度不同

收集一些在專案開發期間常聽到的話語,用類似的語意表達如下:

●專案經理:專案的目標在於系統的準時驗收,任何可能影響時程的額外活動最好避免。
●前來支援的架構師:我已建立好可以執行的基礎架構,只要這樣這樣使用就可以。至於元件要怎麼安排、程式怎麼規定,那是專案團隊的事。
●系統分析人員:每個交易或功能的需求訪談都已經完成,並彙整到各自的需求文件裡。
●系統設計人員:根據每個交易的需求文件,已經設計並開發好對應的程式。同時單元測試過的內容都正常。

表面上看起來沒什麼問題,對不?每個人都做好每個人自己應做的工作。可惜的是,即使每個人都努力地讓產出滿足驗收清單上的每個檢核項目,卻還是有些莫名其妙的問題跑出來、客戶也會莫名其妙地抱怨系統品質不大好、每個人也莫外其妙地幾乎不懂別人負責的部分。

專案裡的產出,不管是文件還是程式都有許多共用的部分,每個人都用自己的角度處理共用的部分當然很容易令其他人難以使用。共用的地方最好是可以符合全部人都方便使用,那是需要投入時間去整理與調整(設計)的;但是"為了公眾的利益去作設計,會影響到投入自己的工作的時間"卻是一種衝突,在管理上不去注重共用的部分而強調個人產出,終究會使大家只留意被要求的地方。

我在N年前也還是可以用極快的速度達成個人產出目標的,不過每次完成工作後再回頭為新需求修改程式時,總是發現內部已經錯綜複雜到難以變動;這樣的經驗累積幾次後終於覺悟自己還缺少了些什麼。現在的我在進行某個步驟時,都會額外花一些時間去整理共用的部分,但是在與其他較年輕的人討論類似話題時,總是發現他們雖已知道現況有些窒礙而還看不到我所注重的地方。

我並不清楚每個人看待的方向是訓練出來或者是依檢查標準而定,但是消極地不想去碰灰色地帶的心情卻感受得到。每戶人家都自掃門前雪時,共用的通路肯定是不會有人去清理的!

2009年6月26日 星期五

W23 做人的方法(16)──態度與高度決定一切

態度:接收到外來資訊後能與自己想法比對並朝最大的最佳化加以調整。越自我的人越不容易改變。
高度:從不同人的角度來看待一件事物並朝對最多人有利的方向改變。越自我的人越無法從別人的角度看待事物。

這些當然是我自己的解釋,不過卻是從一些事件裡得到的結論。

團隊裡有位超強的工程師,能夠快速地選擇對的技術作出功能,也能夠快速地判斷問題提供作法;聽起來一切都很好,但是唯一的問題是方法沒有適當切割且沒有註解,就造成沒有人的理解跟得上他的思緒。有位專案經理跟他說,寫出讓其他團隊成員可以快速理解的註解與寫法,讓多一點人瞭解他的產出未來可以讓自己輕鬆一點,他回答道:我不會UML,也不會寫敍述性的文件。

2009/05與大主管面談時提到對現有設計的改善作法,他說能夠瞭解我想法帶來的好處,稍晚他安排我與兩位資深工程師討論我的想法。在聽過我的敍述之後,他們說:我們可以聽懂你所想要做的事,但是看不到好處在哪裡?我的作法對做事的人來說是浪費一些時間來換取更多的人快速知道自己做了什麼,看不到好處的意思很可能是因為他們並未從旁人的角度來看待。

前面的工程師留下了很多作品,包括我之前維護的那個產品;這個產品應用在專案時的使用門檻很高,目前被重用的其他工程師都花了很多時間去摸索程式碼。他們異口同聲地說,想要深入瞭解這個產品沒有捷徑,一定要自己下苦功。不過在面對同樣沒有註解且風格與自己不同的程式碼時,那位工程師終於說:看不懂他寫的程式!

有些懷疑現在的大師們是否都在開發上具有強大的能力,以至於他們從未完整維護過全由別人所寫的程式碼?賦予自己的責任是開發系統時自然會專注在這個課題;至於怎樣讓別人容易維護程式碼?管他的,反正我自己維護完全沒有問題而且不會找我去做。

2009年6月25日 星期四

W22 成敗的關鍵(1)──開發速度的考量

專案裡需要一個分析資料結構的功能,該結構裡的ParentDataModel與ChileDataModel是從屬關係,但是都具有相同的屬性存取方法,需要寫的功能是處理多個屬性的集合,從兩種Data Model取得屬性的設定值來檢查是否Class字串。

由於這個功能時間上比較趕,撰寫ParentDataModel的時候就直接只思考它的特性,將完成該方法的功能填在一個Method裡。等到撰寫ChildDataModel的時候發現與前一個方法似乎只有傳入的物件不同而已,但是再抽取前一個方法似乎要浪費一些時間,於是copy-paste後再修改了幾個地方。用極快的速度寫好這個取得屬性的功能。


認真地分析一下,這兩個方法只是傳入的物件與紅色框內的程式碼不同,其他的內容幾乎一樣,裡面包含了兩個分解動作與一個迴圈。開發最快的方式是像前面一樣直接複製後修改物件名程,但是就有一堆重覆的程式碼。理想的作法應該再切分為兩個小方法並串連呼叫才對。下面是把分解動作另外抽出方法的結果,同樣是沒有註解的程式碼卻具有很高的可讀性。


之前提過曾把一個元件的View與Controller寫在一起,寫的時候連測試用了三小時,改寫時連測試又多花五個小時,但是一開始就分開撰寫的話估計應要五個小時;概略計算的話,如果沒有修改可以省兩個小時,修改時要多花五個小時。觀點拉到有100個元件的系統來看,假設未來因需要而有20個需要重構。直接作的時候省下100 X 2 = 200小時,另外花費20 X 5 = 100小時重構,整個開發與測試的投入反而省下100個小時。

這是專案開發裡的數字迷思,缺乏彈性的快速設計反而會比設計充滿彈性的作法快200小時,即使面對需求改變還是快了100小時。以前我估計的設計時間常被說為留有buffer,因為別人估兩天可完成的東西我需要五天;如果你是專案經理的話,會用什麼角度思考而出抉擇呢?

2009年6月24日 星期三

W21 記錄的設計(2)──交易記錄Data Model的集合與應用

經過適當的設計,我們可以從server的Log檔案裡用工具取得所有使用者的交易記錄。雖然從電子日誌的資料庫也可以拿到類似的項目,但是在一個嚴格控管的環境裡並不是隨時都可以接觸到資料庫且隨心所欲地下達想要作的指令。

分析出來的交易記錄可以用人、事、時、地、物各種不同的角度去篩選範圍內的資料,如果篩選的條件有適當的分群可以有更方便的應用,像可選擇從個人角度與單位角度去篩選資料。對於一個時間範圍內(通常是給定的Log記錄範圍),較常見的需求是交易的執行種類、執行次數與執行時間,這可以反應系統的使用效益與效能,也可能被要求作成統計圖表以方便查看。

就曾因交易執行時間過長的問題被要求分析Log,當時拿了五個工作天之間的四台server與十台client的Log記錄後,利用之前寫的分析工具來處理上千萬行的Log記錄,最後得到了客戶反應時間慢的區間內的各種數據用以佐證對於問題所在的推測。另外,查看個人在某個時間點所作的事情序列,也是除錯時重要的資訊。

概略說之,全部Log記錄檔還是以Data Model的方式來看待,裡面包含的每行Log記錄組合為一個個的交易記錄Data Model後,就能夠像是搜尋資料庫一般篩選出任何需要的資訊──當然,前提是Log記錄的內容要能確切反應出該時間點的系統執行資訊。

2009年6月23日 星期二

W20 現在的執行位置──getClass()與getMethod()

在Log裡的記錄需要註明程式的出處時,一定會用到getClass(),經由這個方法可以取得Class名稱;但是再細部定位到程式的發生點時就只能拿到行號,然後再找程式碼看看那一行是在做什麼事。

為什麼拿到的資訊不是Method Name而是與程式碼內容無關的行號呢?在執行時有getClass()與this可以取得現在執行的物件資訊、卻沒有getRunningMethod()與thisMethos(二者均為虛構)等取得現在執行方法的功能呢?推測應該與程式設計者較注重結果卻不注重過程有關係──因為只要知道確實在哪裡出錯就好,不需要知道原來想要做的目的。

若去詢問什麼樣的程式碼需要被抽取到Method存放?一個很尋常的答案是"發現某一段程式碼在其他地方存在且完全相同時",這就是我感到奇怪的地方。檢查兩段程式碼是否相同的觀點僅是查看其外表,但是我一直認為程式碼被群聚為Method是它們屬於分解動作之一才會被提出的。重構的觀點提到"有一行註解之下的一段程式碼可以抽出為一個方法",這比較接近事實,不過要如何保證一行註解之下的是"一段程式"而不是"三段程式"?

Method抽取時依這樣的概略原則拿到的可能是錯誤的對應:或許是應該在一起的程式碼被拆開,或者是不該在一起的被放在一起,這些都會在某些場合下造成系統的複雜調整。當每個Method都是依照負責某個行為的目的而被建立起來之後,才會有對getRunningMethod()的需要。

註:目前的getRunningMethod()可以利用Exception裡的printTraceStach()來達成。將這個內容輸出為字串,就可以解析出指定位置的完整method call stack,利用此方法可以省下在很多方法內插入Log的時間。

2009年6月22日 星期一

W19 記錄的設計(1)──記錄行與交易記錄Data Model

系統執行狀態與歷史的內容能夠提供許多分析的資訊,這些資訊我們習慣在執行中隨時記錄為Log。不過Log的特性是一次記錄一筆與上下文無關的文字,所以如何在一行文字間涵蓋到重要的資訊以及建立起前後Log的關聯,是定義記錄內容的重要關鍵。

對單行記錄而言,人、事、時、地、物五個基本資訊加上想要記錄的內容應是最基本的構成。記錄的內容原則上是由能識別系統行為的關鍵字,加上進行該動作時所需的相關資訊所組成;這部份最好是格式化為固定字串,能夠轉換為基本Data Model會較容易處理。

在一個交易期間會有多行的記錄存在,因此要定義交易開始與結束的記錄內容,夾在這兩行之間的表示為同一個交易期間。以範圍框住同一交易的想法雖然沒錯,但是遇到多執行緒執行交易時會發生兩個交易內容混雜產出的問題,因此記錄行必須再定義一個交易序號的資訊以供識別。此外多行交易記錄經過處理應該能夠取得一個交易記錄Data Model,具有從交易方面看待時應有的執行屬性。

記錄行的意義與達成目標的分解動作是很相似的,記錄的是單一動作的執行內容;交易記錄的意義則像是功能的定義,在裡面可取得執行時的相關資訊。記錄行主要提供的是有沒有進行該項動作、有沒有錯誤發生,交易記錄主要提供的是該交易的基本資訊、輸出輸入資訊與結果;前者是系統人員所檢視,後者則應用在使用交易的記錄分析。

2009年6月20日 星期六

W18 快速開發的工具(3)──進化為系統分析工具(SA Tool)

限制整個Workspace的UI編輯工具為只能編輯專案層級的元件時,編輯的對象就會只有Use Case下的流程,選用的範圍則是該tier提供的Activity。執行流程的定義只是SA的一部分,所有與交易相關的定義都應該被容納到系統分析工具裡才是完整的SA Tool。

這表示交易相關的輸入與輸出都要被考慮,像是畫面的設計、列印的格式、主機電文的切割定義、主流程的功能選擇、輸出入欄位與Context的對應……等等都應該呈現在工具裡。另外整個系統的設計也應該被定義,像是角色的定義、交易的定義、資料的定義、以及任二者之間的關聯……等等。SA Tool的好處在於進行需求訪談的同時,可以利用系統收集全部相關資訊並立即使用,在快速定義交易的同時盡量避免產生一些已知的問題。

上述每一個編輯項目都應該要能嵌裝與拆解,施行時視系統實際的架構關聯來設定SA Tool應該具有哪些模組。這是一個更龐大的工程,系統開發時所需要的一切概念組成的架構與關係需要精確地定義與設定,再根據這個架構去設計呈現的UI編輯工具;我們當然明白,只要那個架構有所變更就會影響到SA Tool需要再度改寫。換句話說,除非能夠研究出開發系統時會有的所有定義與關聯,要不然SA Tool應該是個會被改來改去的玩具,或者是寫死為只適用在自己專案裡某部分的特定工具而已。

"可被遞迴處理的元件結構"是對程式設計的目標,"通用的系統分析工具"則是對系統開發的理想。縱使這兩個想法的背後都需要艱難的分析設計與實作,不過若能確認能夠解決大部分的現有問題而沒有邏輯上的破綻,這個決心是不會變的。

2009年6月19日 星期五

W17 快速開發的工具(2)──Project、Workspace元件的UI編輯工具

Component結構UI編輯工具是針對單一元件的設計,但是將系統用tier與layer切割之後會發現每一個格子都具有相同的特性,與同一層及自己繼承的元件都擁有類似的關聯。根據這個特性,只要詳加定義可以使用的範圍,就可以在指定編輯某一格子時只顯示出有關聯的其他資訊。

設定關聯後顯示相關的範圍是這個想法的核心。目前的想法是用一個設定工具將Workspace內的每個Project都對應到一個tier-layer切割出來的格子,每個格子可以引用的範圍是同layer、同tier的合部與同tier、下一個layer的全部;然後再更進一步地對每一個格子裡的Package定義使用關聯,不在使用方向下的Package都是不能呼叫的。

編輯工具的元件索引區應列示現有的所有元件以及它們所在的格子,選擇一個元件就會開啟它的結構與方法列表;選擇一個方法後在旁邊列出所有可選用的元件與API,可以為方法繪製流程圖並引用那些元件方法與API來組裝。依此模式來設計全部元件的每一個方法。

與註解有關的功能部分可以加在這個工具裡,像是編輯Class或Method的註解與自動產生基本資訊等。或許還有更多的部分還可以加上,目前對於這個龐大的工具只有初步的輪廓知道可行,但是還沒有很清楚地定義各個細節。

2009年6月18日 星期四

W16 快速開發的工具(1)──Component結構UI編輯工具

在資訊業裡的大多數人員都有著一個夢想:是不是哪天能夠出現一個工具,只要根據需求勾選一些選項或是畫出流程圖後,就可以自動產出能夠達成功能且具有一定品質的程式?雖然絕大多數的人覺得這像是被工作壓榨過度後的囈語,但是我這兩來年在心裡盤算與推演卻感覺那是有機會達成的事。

回憶一下設計一個小方法時自己的行為。首先先根據需要所要處理的資料來定義處理的迴圈、決定每層迴圈內所要執行判斷條件或動作、針對每個動作挑選出最適合的API方法、最後再將處理結果或例外傳出去。簡單地看,設計的過程只有流程順序的定義與動作方法的選取而已,不是嗎?(不存在的動作方法可以宣告一個新的,再回歸為選取)

印度公司的電文處理流程定義與IBM的SOA概念,多少都幫助我更完整地思考這個可能性。想像有那麼一個元件設計的UI工具,在設計方法時的主要編輯區是繪製流程圖的區域,我們可以先在這裡決定方法內處理迴圈,之後要定義處理動作時右邊有個區域列出允許呼叫的全部元件方法與API,只要從中挑選出想要使用來組裝。(宣告與給予初值也視為一種動作)

對於程式人員來說,用工具來產生程式的效率實在是慢太多了,倒不如直接寫程式省事。用工具的好處是可以降低學習的門檻、限定使用方法的範圍並產出結構與寫法一致的程式碼,這都是寫程式時難以達成的標準。把程式碼倒回工具產生流程圖倒也可以,順便篩選那些無法倒回流程圖的程式,那表示撰寫時並沒有按照一定的規範。

2009年6月17日 星期三

W15 令人感到麻煩的設計(16)──固定值與固定常數的意義

在設計與實作的時候,所有的固定數值與字串都應該被抽取為常數,使用的時候就將固定數值與字串置換為該常數。原本我也依這個方式使用常數,但是不久前製作計算function points工具程式時卻發現不同的用法。

上下傳電文欄位是功能點計算的來源之一,第一個專案的電文定義檔命名規則是在交易代號後加上u或d來表示,因此將上下傳檔名另外用常數代表,取檔時就使用txnNo+UPLOAD_FILENAME+".xml"來組成完整檔名。程式拿到第二個專案就發生了錯誤,因為檔名是以別的名稱識別,只好直接修改常數內容讓程式可以正常執行。

依照事對物的想法,有UPLOAD_FILENAME的存在就應該對應存在一個getUploadFilename()的事存在,之前在檔名裡直接嵌入UPLOAD_FILENAME的作法是一種綑綁。"取得上傳的檔名"應是一件事,其中包含的是一種規則,雖然在大多數的情況下規則是直接取得UPLOAD_FILENAME,但是規則總有變的一天,將改變的規則限定在一個給定的方法裡可以增加設計的彈性。

那個工具程式後來作了一個改變:收集所有在專案裡可能有所不同的規則,定義一個Interface容納取得那些規則的方法,看要執行哪一個專案的內容就使用該專案特有的規則定義。

得到了寶貴的經驗:每個常數都應該轉換為具有該層級元件意義的名稱與存取方法,以便操作時明白其含義。呼叫時可直接在參數裡引用常數,但是在取用時要使用存取方法,因為取用時隱含規則,規則都可能改變。

2009年6月16日 星期二

W14 專案的開發(6)──Component的方法設計

每一個Component Interface Method都可被視為該元件的一個Use Case,在設計時同樣以輸出輸入為起點,再決定內部的處理流程與動作。

設計中若需有其他元件的功能支援,必須是位居同一層次的元件才可以;讓同一次層次負責該項功能的元件集中管理達成功能所需的資源,可以有效減少上下層的交互關聯。如何明確切割出有對應意義的分解動作、動作要放置自己這一層還是父元件,這個作法的好壞會影響到元件變更時的彈性。應切出方法的動作沒切分而直接實作在原有方法裡,會讓多個動作混在一個方法裡;應放置在父元件的方法沒拉上去,會令方法的功能對應在錯誤的層級上。

從上而下將所有元件的方法逐一設計出來、收集每個元件內的所有方法、記錄每個元件方法與其他元件方法的關聯,這些是設計階段所要做到的事。將方法的設計依"達成目標的SOP”之思維組織,並將動作放置在正確對應的位置,會得到較有組織化的程式結構。

看過物件導向程式的九個規則這篇文章,看起來像是從程式碼的呈現上去避免或改善一些寫法。比起"看到某種狀況就改用另一種方式做",倒不如"依其需求的想法作出對應的寫法"能夠更教人明瞭其中含義。例如以下列出的幾個規則:
●每個函式裡面只能有一層縮排,如果需要多一層,請多寫一個Method去呼叫
●不要使用else這個關鍵字
●所有基本型別都包裝成物件
●保持東西輕薄(Class不超過50行,每個Package不超過10個檔案)
●使用第一級collections

遞迴設計所有流程元件的每一個方法,直到剩下要實作功能元件為止;功能元件應由該功能領域的專家設計實作或是由元件庫裡挑選適合的元件使用。經過基本設計概念課程與系統領域教學的每一個人,應該都有能力進行流程元件的設計。

2009年6月15日 星期一

W13 專案的開發(5)──決定完整的Component Diagram

首先要進行整個系統的元件佈置。一開始先參考架構先定義好系統的tier,再根據L05的切割層級(參考上圖)來定義layer,如此可以得到一張矩陣圖。

●分析的Component Interface必須放在專案的那一層,根據發生的地點放到對應的tier裡
●研判是否有相似度很高的Interface或Interface Method,在此先作調整
●每一個Component Interface都要繼承一個上一層級的Component Interface
●循環繼承到最基本那一層,不能有跳層或是沒有繼承的Component Interface
●一直review這張圖直到大家都沒有異議為止

系統這張Component Diagram非常地重要,因為一旦要移動位置或是調整繼承的話,基於這個靜態佈置上所作的動作設計全部會因架構變動而重新調整,影響是非常巨大的。還有在設計的時候每個元件都只需要放置一個Component Interface就好,不用顯示方法(方法可以在接下來的階段移動)。

定稿之後,可以再使用一個小工具讀出Component Diagram內所有的Component Interface與繼承關係,再呼叫Component產生工具一次鋪設出所有的元件結構。只要這個元件結構能適應未來的變動,那麼一切的工作都只有在元件與其繼承的元件內部設計而已。

2009年6月13日 星期六

W12 自動收集或產出系統需要的資訊

在開發階段會陸續建立新的message id,一般作法是用人工將新的message id貼到properties檔案後再放上詳細的訊息內容。漏掉時就看哪一個message id不存在再另外補上。

現行的ResouceBundle作法令訊息檔的內容只能讀出而無法寫入,在基本Data Model加入PropertiesDataModelInterface為了改善這個現象,讓訊息檔可以新增並寫入。基於這個設計可以再往上包裝一個訊息元件,在開發與測試時設定參數為可增加模式,把找不到對應訊息的message id加入後立即寫回properties;在正式執行時再設定為ResouceBundle模式加快速度。

系統開發的時候還有很多自動化收集的機會,只要是用人工重覆輸入的都有機會寫出輔助工具。像是系統的參數設定也可以設計類似的作法予以自動新增、收集後群組化的Data可以利用工具自動轉出資料庫用的DDL檔案、Actor列表與Use Case列表、Actor與Use Case使用關聯可以產出權限定義資訊……等等,還有之前提過將所有Activity對應設定為Component Interface Method後再自動產出Component Interface。

分析完這次Iteration裡所有Activity並定義妥全部Component Interface後,就可以進入設計階段。

2009年6月12日 星期五

W11 專案的開發(4)──Use Case的結果與訊息

做每一件事都有成功的結果或是失敗的原因,這同樣是每一個Activity所應考慮的範圍。使用者操作時必須明白操作的結果,因此每一個Activity都應該要有各自的回傳結果,在Use Case結束時顯示相關訊息讓使用者看到。

底層的錯誤要到設計時才會詳細定義,但是在使用者端還是會有明確的狀態可收集。像從密碼輸入器輸入密碼來說,使用者很明顯地可以考慮到沒有接續、無法按鍵、逾時未輸入等等的錯誤,這些都應先收集起來成為message id。

message id最好是每個Activity都擁有自己的編號而不要共用,這樣做的目的是可以在一拿到編號就知道是在哪裡發生的問題;當然,不同的id可以指定顯示相同的名稱,因為使用者只是需要知道有錯而不一定要知道在哪裡錯。密碼輸入器與鍵盤輸入的逾時理應有兩個不同的message id但是在顯示時可以只有"密碼輸入逾時"一種敍述。如果二者的message id都相同,要是哪天客戶希望二者顯示的訊息不同時,又會令人跳腳。

軟體的複雜度其實有很多是人為造成的,其中又有很大部分是把多個來源混雜成為一個定義。在系統沒有變動的時候,因為執行的結果全都正確所以這種定義並沒有問題;但若有問題或變更造成的修改落在原來的多個來源得分割為不同的定義時,真的是改的越底層就陣亡越多人。忠實地用一個定義對應一個來源是降低這類風險的最佳作法。

讓每個Activity都只擁有自己的message id就毋需製作追溯表,因為這只是一對多的擁有關係。message id與實際顯示的訊息內容是多對一的關聯,不過在實作時會因為properties的作法而被攤平為一對一。

2009年6月11日 星期四

W10 專案的開發(3)──擬定流程與收集Activity、Data

統合看待已經找出的Use Case,接下來要詳細分析每一個Use Case內部的必要執行步驟(視為該Use Case的SOP)。進行之前的前置作業是準備系統詞彙表的收集處,這在進行分析時會逐步新增。

盡可能地收集每一個Use Case的完整執行劇本,定義出劇本中的分解動作,請記得要以使用者的角度來看待每一個Activity,同時從數個劇本中分析出必要流程與選擇流程,盡可能製作為一張Activity Diagram。只製作成一張Activity的目的是在後面要直接轉成程式。

每一個Activity如果牽涉到資料的變動,就從需求文件裡找出定義的名詞,一方面加入系統詞彙表,一方面註明Activity-Data的關聯。(目前的UML沒有這個記錄,只好自己作Activity-Data的垂直追溯表)分析完全部的Use Case後,收集的系統詞彙表可以用CRC Card的方法定義彼此的關聯,藉以產出Data Model的定義。

繪製Activity Diagram的時候如果Activity已經有了"絕對"要使用已經存在的,因為那才是實際有的關聯;如果畫成兩個就會讓關聯分開到兩邊而產生不正確的結果。Activity的package佈置就偏向設計,將類似作用的動作集合到同一個群組裡。像上面兩種密碼輸入設備的Use Case若Activity放在同一個package就較容易看出相似性。

Use Case下的Activity Diagram裡擁有的Activity可以產生Use Case-Activity垂直追溯表,也可以產生流程程式;記錄Activity-Data的關聯,可以產生Activity-Data的垂直追溯表。根據一層層的關聯,最後可以得到Use Case-Data的垂直追溯表,就能夠清楚地知道二者的完整關聯。

2009年6月10日 星期三

W09 專案的開發(2)──決定系統的主要架構

Use Case收集到一個程度,需要開始分析Use Case間的關聯,目的是檢查被定義為include或extends次數多的Use Case將之納入主要架構的設計。

直接查看Use Case的關聯(項目)看哪些拉出很多關係當然可以,不過製作出Use Case-Use Case水平追溯表(清單)會更有一目瞭然的效果。UML工具應要支援關聯的記錄與存取的API,那麼就可以撰寫工具程式直接產出為Excel檔案並註明include或extends的關係,再運用Excel的功能作排序、計數等等的運用找出適合納入主要架構的Use Case。

在主要架構裡會有些隱含的Use Case。像是在client-server架構下,如果沒有採用現成的framework勢必要自己設計一個收送的功能,即使客戶沒提也一定要有記錄log的功能,這些都是必須額外加上的Use Case。在這裡加上的Use Case大多與設計有關,必須視使用的framework予以調整到完備。另外還有一些event時間點的處理功能,也要一併納入考慮。

從UML model裡再轉換出Actor-Use Case的垂直追溯表,可以定義出執行功能的權限管理。在分析階段收集範圍後再追溯關聯,可以精確地得到相關資訊作為架構設計的依據。

2009年6月9日 星期二

W08 專案的開發(1)──收集Use Case

在開始之前,先讓觀念作點轉換:
人=Actor
事=Use Case
物=Data
時=Use Case Entry Point
地=Use Case Precondition

在系統分析時對Actor與Use Case應進行的步驟如下:
●收集全部Actor與Use Case以明確定義系統可以做的所有功能
●建立全部Actor與Use Case之間的使用關係
●分析Actor之間的使用範疇有包含關係者改為繼承
●所有Actor區分群組並定義在其所屬的Package下
●分析Use Case之間的使用範疇有包含關係者改為繼承
●分析Use Case內是否能拆解出include或extends關係的子Use Case
●所有Use Case區分群組並定義在其所屬的Package下
●明確定義每一個Use Case的發動時機
●明確定義每一個Use Case發動前的狀態
●明確定義每一個Use Case發動後的狀態
●收集每一個Use Case的所有可能劇本

在抽取共用Use Case的同時,思考的是"某種特定的使用者目的"。例如最近幫客戶設計晶片卡密碼驗證的功能,其目的是把使用者從密碼輸入器輸入的密碼傳到晶片卡模組去檢查,這雖然可以是一個獨立的Use Case,但也可以將用途切割為"取得密碼輸入器上輸入的密碼"與"將密碼送到晶片卡模組檢查"兩個Use Case,後者include前者。如果輸入密碼的方式又多了"從鍵盤輸入密碼",由於是二擇一的關係,關聯就要更改為extends。

對設計者而言,Use Case與Activity的抽取常會混淆,Use Case必須從Actor的角度看待,其目的是"對使用者有意義的獨立行為"。上面若將不同的密碼輸入功能合併為一個"從輸入設備輸入密碼"就會有點模糊,因為輸入設備只是個概括的名詞(除非再另外定義輸入設備包括哪些);若在其他功能內只允許用密碼輸入器輸入密碼時,這個Use Case就會變為不合用,重新定一個會造成功能重覆,修改原來的又怕會影響之前的Use Case造成問題。

沒有作好一對一的分析與設計,就常會有這樣的兩難局面。

2009年6月8日 星期一

W07 一般工程設計與軟體設計的不同

最近參與公司內的討論,聽到了"因為軟體本質上的不同,所以一般工程方法難以適用在軟體工程"的話語。發言者強調軟體是易變的,所以無法像工廠一樣產出固定不變的元件,同時設計也常因需求改變而隨時變動,建立在易變產出上的方法自然無法固定。

依照慣例,我還是仔細地思考了一下話中的含意。

根據自己的經驗推論,建築工程是三度空間的設計,但是軟體設計就像是四度空間的思考。像建設高速公路時只要在必要的通道上設收費站就一定收得到過路費,但是軟體即使規定好設計的層次,但任誰都可以修改為完全不經過那一個點而進入;汽車的動力從引擎經過傳動軸再傳到輪胎,但是在程式定義上直接讓動力直接轉動輪胎也是可行的作法;還有就是蓋房子時九樓往上一層一定是十樓,但是在軟體裡卻可以跳躍到任何一層,完全不受空間限制。

就製作的成本考量,一般工程需要零件時因為自己開模製作很慢於是會開好規格訂購現成零件,但是軟體元件卻因程式修改快速且成本不大,所以就在該要的地方直接作出可以應付的結果,全然不去考慮日後重用的兼容問題,反正複製一個後再改程式微調差異即可。

只注重結果的思維造成跳躍式的使用、零成本的修改(指非人力的)造成隨意的改變,雖然執行的結果相同,卻使得產出的程式碼有難以統一的多變風格、修改時也不易找出問題的真正原因。也由於同樣的需求會觸發各種不同的想法與設計,我們又無法像一般工程那樣證明哪個作法是最好的而規定下來,更增加了產出的不確定因素。

邏輯上只要檢查不出錯誤就可以說是對的。在沒法證明哪一種作法具有全部好處時,大家只能針對自己著重的方向使用自己覺得好處最多的方法。然而在對應種類眾多的時刻,選擇作法時比照一般工程嚴謹地讓每個零件限定只能接續特定的零件,是否就能適用一般工程的作法呢?

2009年6月6日 星期六

W06 做事的方法(16)──對小朋友所說的做事接物摘要

●使用一個物時一定會有一個連帶的動詞,該動詞稱之為一件事。
 例如:拿剪刀、穿鞋子、背書包等。
●物要固定放在一個地方,需要的時候只要找一個地方;如果平時物隨意亂放,需要的時候就得找很多地方,而且不一定找得到。
●每一件事都有一系列的動作要做,而且要依照一定的順序。
 例如:用剪刀時要將拇指與食指伸入剪刀的圈圈、把要剪的東西放在另一側的兩個剪片之間、拇指與食指用力夾起。
●直接使用一個物的事稱之為小事,包含數個小事的事稱為大事。
 例如:整理書桌是大事,因為包括了整理小書架、桌面、抽屜與桌下等四件小事。
●指派一件事時要有能力把它拆解為小一點的事,並各自一直循環到最小的事為止。
 例如:要你整理房間時,意思是要你整理書桌、整理書櫃、整理衣櫥與清掃地面。
●指派給你的大事拆解的定義要跟我想的一樣;當然,會先告訴你定義的範圍是什麼。
 例如:要你整理房間時,不要只整理好書桌就回報說已經做好了。
●做事的範圍要看實際的物有些什麼而加以變動。
 例如:現在的書桌上有小書架,如果換了一個沒有小書架的新書桌,整理書桌時就不用整理小書架。
●要能判斷自己應該做的事是否需要立即動手去做。
 例如:感覺書桌有點亂時,不需要人講就應該自己收拾;睡前與出門前檢查應帶的東西是否都已放入書包。

生活上接觸得到的事物很多,要一件件都在適當的時機做而且要做得好,實在是不容易的事;大人都已經是如此,更何況心性未定的小朋友。整理出做事與接物的原則雖然可以比較明白其原理,但是對真正要做事的細節並沒有很大的幫助,因為會忘的還是會忘、不做的終究不做。理論與實作,不就是像這樣的對照嗎?

2009年6月5日 星期五

W05 三看簡單型Component

運用人、事、物的想法再回頭來檢視簡單型Component。如果直接可以操作物的只有最小的事,那麼先將之定義為Action,其他無法直接操作到物的事則是不同層級的Flow;這麼一來除了最底層的動作要包裝為功能元件之外,其他各層級的流程應該都只是流程元件?!

這樣的切割法又讓我對元件的結構有了不同想法。依這裡的思緒我應該包裝出只有Action的功能元件,使用Action的流程再另外包裝出流程元件;不過在特定需求的系統裡我也可以產出一個包裝不同流程元件與功能元件的複合式元件,依參數調整內部使用的流程元件;流程元件操作功能元件時,也可以依設定更換不同實作的功能元件。這樣的作法將可以提供更靈活、更多樣化的組裝。

程式解析工具在元件分離的情況下依然可以運作:流程元件只需要處理流程方法是原本就要做的,動作方法不需要做所以不存在也沒關係;但是功能元件內部就需要再拆解出與執行流程相關的方法,標示給工具去解析。不過基本上並沒有很大的影響。(唯有元件產生工具需要調整)

註:人、事、物的關聯一開始只是為了對小朋友講解做人做事的基本原則所整理出來的想法,卻沒料到完全適用在元件結構的應用,這是原本始料未及的。

2009年6月4日 星期四

W04 我的設計準則(6)──追溯關聯

所有的單位在操作或使用的同時就發生了關聯。無論是一對一、一對多、多對一或是多對多,關聯都是追蹤影響的最重要依據;唯有透過關聯的追蹤才能發現某個單位的變動範圍到底有多廣泛。

收集範圍與追溯關聯是在求快速開發的同時最易忽略不做的工作。由於程式碼經常變動造成使用關聯頻繁變化,使得修改追溯表的速度根本追不上變動的頻率,長久下來完全沒有人可以追蹤出任一個底層API修改之後向上隔了數個層次的Use Case到底有哪些會被影響。縱使現在的IDE工具可以查出全部的呼叫歷程,但是在出版本前的迴歸測試要如何取得幾十個變更的影響範圍?

無論人對事、事對物、地對事、時對人與事或是事與事之間的全部關聯都應該要記錄下來,不管是做事的SOP或是程式的呼叫也都有使用的關聯,如何快速列出能夠操作目標單位的全部集合雖然不易,卻是在某些狀況下必須得到的結果。程式碼中快速且正確地列出使用集合,正是我要定義程式結構的主要目的。

針對UML裡的Use Case我實測了三種UML軟體:Rose 7.0.0.0、JUDE Community 5.5與Star UML 5.0.2.1570,測試範圍是Actor與Use Case的關聯、Activity與Activity的關聯。Rose明確地標示出三種圖示與兩種關聯;JUDE連Activity都沒被視為保存的單位(這表示activity無法分析是否重用),更不用提關聯;Star UML標示出三種圖示,也在各圖示內管理自己的關聯。

忽略多個Use Case之間的使用關係時,雖然對於單一Use Case可以快速開發,但是就沒法作出最佳化的分析與設計(如果向下的層次也不追蹤使用關係的話)。使用關聯的雜亂是專案產出的品質難以控管的重要因素之一,卻是被許多人所忽略的。

2009年6月3日 星期三

W03 我的設計準則(5)──收集範圍

分析設計時的人、事、時、地、物都需要各自收集範圍以便定義各自的邊界,收集的好處在於接觸到某一個單位的同時能夠立即知道它是否在該單位的集合,已存在的話可以直接重用,不存在的話就必須另外建立。這個方式對於reuse的幫助很大。

收集起來的單位數會非常多,因而需要建立各自的分類方法依特性分群組放置,這樣可以加快定位的速度;當然加上搜尋的功能也很好。在"事"的分類佈置可以衍伸為Package的定義,搭配Component工具就能夠將所有新"事"的定義直接匯出元件程式結構,舊"事"則依設定參考原有的元件功能。

觀察到很多SA、SD與PG的想法都偏向輸入與輸出,在意得到什麼樣的輸入且應有什麼樣的輸出而忽略了處理輸入輸出的做事流程。在明確定義做事流程的方式下可以得到處理的SOP,進而與其他做事流程共同抽取相同的部分成為API;忽略做事流程的作法下任何想法都以湊出對的結果為目標,雖然效率很好,但是僵化、綑綁與難修改都會跟隨其中。

這是可以套用在任何地方的想法。分析時一個群組(例如會計室)收集的應做之事就是該群組的功能,一個單位(例如出納)收集的應做之事就是這個單位的責任;每一個群組與每一個單位都依其特性給予它應有的責任範圍,並要求該責任只有這個單位具有而其他單位都不得處理。

每個部份要先依其大小意義定出有多少層次,在收集範圍的同時都要依層級分開收集在不同的層次,唯有將所有單位都分門別類地放置的一致化作法才能夠再定義出更上一層的存取規則。這也正是各種工具程式的操作依據。

2009年6月2日 星期二

W02 Use Case裡的人、事、時、地、物


"人"必須透過"事"才能操作"物"是分析與設計的根本,某個物被改變"必然"是因為某個人做了某件事,確定了這個關係後就可以很容易地接受Actor、Use Case與Data的說法。再加上該件事發生的時機與執行的地方,就構成了人、事、時、地、物的思考模式。

在系統裡通常會用角色來取代Actor,當某個角色發動做某件事時,權限控管會根據角色與功能的關係決定是否允許。功能被執行後會進行一連串的動作,執行動作的流程會被編輯為Activity Diagram,而被執行的動作就是Activity。


事件可以區分為一次操作一物的"小事"與一次包括許多小事的"大事"。舉例來說,整理書房是一件很大的事,至少包含整理書桌、整理書櫃、清掃地面幾件較小的事;整理書桌又包含整理書架、整理桌面與整理抽屜三件明確的小事,每件小事都關聯到一個物件。切割範圍的思維一致後,每個人對事物切割範圍的誤差才不致大大。

單位切割之後要處理的是每個單位的分類方式,如何訂定各種存放類別、單位存放時如何分類也是必須同步的想法;另外單位之間原有存在的關聯都是必須保存下來的資訊。

2009年6月1日 星期一

W01 公司應有的文化──所有人員的想法與行為一致

公司的CMMI導入團隊說現在訂立的規範都只強調精神,各專案要根據自己的特性予以調適;由於沒有提供一個基本的範本,造成每個專案各做各的沒有交集,等於是沒有規範。也由於各個專案作法與產出不同,卻擁有相似的問題──沒人看得懂,修改沒品質,無人想維護。

敏捷開發的原則非常強調開發成員間的想法同步。2006年開發UI編輯器時小組裡有四個人,我們每天上午十一點與下午五點都各開半小時內的會議,內容為核對進度、提出問題與回饋作法;目的是讓所有人都對不熟的技術與平台獲得同樣的了解,當時我覺得效果相當好。可是當時的主管有天卻問我這句話:你們一天到晚常常開會,這樣進度來得及嗎?

一間較大公司內會有數個專案,一個較大的專案下會有數個小組,把眼光提升到公司的層級就會有一個疑問:要怎麼做才能讓公司內所有專案、所有小組、所有角色產出的內容與品質接近一致?採用CMMI的流程控管會有許多繁瑣的手續耗費開發的時間,使用快速的方法可以讓開發迅速卻讓其他人難以瞭解系統。

無論是人為的控制或是自由的開發都有可能因為每個人的思考方式不同而導致結果有差異。要能同步所有人的思維,應從瞭解設計的本質開始,再搭配每一個階段、每一個步驟為什麼要這樣做,這樣做有什麼樣的好處與壞處著手。讓所有人打從心裡相信雖然自己多花一點時間,卻能夠留下足夠資訊達成各個面向的要求。

做事方法與思考原則的完全一致化,會訓練出有紀律的團隊,同時會有讓所有成員一看就懂的產出。