2007年7月31日 星期二

C19 Controller的設計(1)──Business Logic


這張圖是我所認定整個系統具有的完整層次。Controller的部分是以淡藍色所顯示的部分。

在Controller裡所要設計的是輸入結束後的系統處理,通稱為Business Logic的部分。第一個進入的Controller是對應Use Case的Activity Diagram的控制物件,本身只負責流程該換到哪個Activity執行而不要有任何其他的實作摻雜在裡頭。

第二層的Activity Controller裡是Activity應有的邏輯控制,在設計的時候同樣先用文字簡單表達處理的步驟,每一個小步驟都對應到一個Controller Service裡的方法。同樣地這裡純粹是控制,不要有其他的處理。有些系統的開發會把這兩層的控制包裝成Operation Flow與Operation Step的交易流程套件來減少額外的開發。

實作的功能都放在Controller Service這一層裡。比對View Service來看,我們可以發現Service才是系統真正放置功能的地方,唯有在這裡才允許存取或操作到不同層級的物件。在Service裡都是處理動作的實作,每個動作的設計都必須決定是要使用現有的Component、自己設計新的適用Component或是自己寫程式在方法裡處理。(這裡的Component必須與Business Logic無關)在確認Service動作的同時也必須要設計或定義使用Component的介面。

驅動程式各部件運作的關鍵層次,就是Service;經由這裡存取Model與View,並達成與外部的連接,一切功能需求裡提到的系統互動動作,大多會在這個層次實現。

2007年7月30日 星期一

C18 View的設計(3)──進入Controller

這層設計的一開始,要先決定View進入Controller的對應事件與傳遞的物件。在一開始的基礎關係,每個功能會有自己的View,自己的Controller,如果不去抽取相同的部分就會得到前面提到的大學生程式類型,造成每個功能都有一大堆重覆的程式碼。我們可以在View與Controller之間使用Façade Design Pattern作為唯一入口,在執行事件的Service裡把View的欄位與值在listener包裝成適當Model傳入Controller。

以Façade類別作為分界點,無論使用哪一種GUI元件開發顯示畫面,只要在進入執行時傳入指定的Model,後面的Controller都可以正常地處理;反過來看,只要Controller都符合傳入Model的介面,把畫面的Listener組成該Controller所需的Model就可以呼叫它。這便是用來應付可能發生的變化的設計應對。

許多經驗較少的人把交易的邏輯寫在顯示的類別裡,在處理資料的時候直接對顯示物件操作。由於指定物件本身的類別名稱,就會造成處理與顯示之間的密合而無法隨意更換,這樣的設計就像是一體成型的機器人手臂;因此我們需要在意義不同的層次上加工製作隔開兩邊的介面物件,藉此來達到可以抽換另一端實際執行物件的活動機制。

2007年7月29日 星期日

C17 做人的方法(4)──人會是Listener

在忙碌的工作裡,我們需要安排自己的工作項目與時間分配。手邊有哪些工作,每個工作的開始日與完成日都要牢記;還要記得所有會議的時間與地點,會議中進行哪些討論,要先準備哪些物品。在工作安排初期與會議敲定的瞬間,心裡都知道有那些事的存在,但是時間點到的時候能保證全部都記得嗎?

任何事都可能會忘記,一旦忘記沒做好接下來可能有重大的影響,許多人會用筆記本記錄未來要處理的所有項目,每天不斷地檢查清單的內容看有哪些項目是現在要做的。這像是使用polling的呼叫模式,每隔一段時間就必須記得去檢查,有狀態的改變時就去處理;但也會有一些時候檢查了也沒有要做的事浪費檢查的時間,或是在該去檢查的時間點沒去檢查而錯過時機。

我喜歡用PDA記錄工作項目與行事曆。該在什麼時間準備什麼樣的資料與做什麼事,全都記錄在行事曆中並設定鬧鐘提醒,時間點一到PDA就會自動提醒該做事項的內容。平常要記的事情已經夠多了,再加上這類瑣碎的事情實在會煩死人;使用行事曆後就能放空這些小事來專心工作,等到PDA提醒該做事的時候,再參照工作項目的內容去處理要做的事實在是輕鬆多了。

面對雜事的時候可以只是單純的Listener等候通知,但是處理工作事務的時候就不能被動成這個樣子。老板們總希望每位員工都是運作良好的Controller,能夠把手邊所有的工作項目理想地安排並完全地掌握所有事項的進度與狀況;這也是每個人都應該做到的基本能力。

2007年7月28日 星期六

C16 View的設計(2)──事件與處理

畫面在顯示之後就等待使用者的輸入與操作。所有程式語言工具對於畫面操作事件都有詳細的規畫與定義,所以大家對Listener介面的使用都很熟悉。這裡想要討論的是Listener的實作與事件對應處理邏輯的關係。

由於操作事件會跟隨著畫面作反應,所以把Listener的實作放在畫面呈現的類別裡是直覺的設計想法。我們發現有時同樣的事件與處理大量地重覆出現在很多畫面(例如系統規定Enter鍵是執行功能的熱鍵),在這樣的情形下相信所有的人會把Listener拉出到一個專門的Class來處理同樣的事件。

現在在這裡最常遇到的問題,是在拉出Listener後仍然把事件發生的對應處理寫在Listener裡面,造成難以切割的密合狀態。這時只要事件與處理邏輯之間的對應關係有所改變,都必須搬動整段程式碼才能達成;就算把這些處理提出到獨立的方法已經有做抽取動作,但也不算是正解。因為,無論如何都會動到程式碼。

如果認真“回憶”的話,我們在需求階段製作的Activity Diagram有分User與System的Swimlane,從User指向System的線條就是event,進去後系統的連鎖Activity正是系統要對應處理邏輯。我們要注意的是,反應由一堆Activity依序組成,每個Activity對應到一個ViewActivity物件的話,管理執行順序的工作交由一個ViewFlow物件處理,Activity的處理內容再交由View Service處理。

右上角的藍色框就是對應每一個event所應該設計的反應機制,當需要再往後執行功能邏輯時,會再往後面的層次傳遞。


2007年7月27日 星期五

C15 做人的方法(3)──把用過的資源放到該放的地方

宣告記憶體佔用,並在使用後釋放乾淨,這種維持系統運作正常的原則,同樣也是人類在日常生活中對於環境應該遵守的準則。

要記得我們多做了不該做的或少做了應該做的事時,周遭的人事物必定會受到相對的影響。例如拿出來用的物品在使用後要放回原處,這樣所有人才能照SOP規定的步驟在該拿到物品的地方取得,今天我們拿了物品不放回去,必然會造成應該取得物品的動作拿不到,同時不該有那項物品的地方多了這件物品。這便是相對的影響。

現在常看到很多人在路上隨手丟煙蒂、紙屑,丟棄後還能夠輕鬆自在地離去,從環境的角度上來看那個地方就多了一項破壞景觀的物品;隨處丟棄不想養的寵物也是這樣,把自己應該處理的事推託給別人或是大環境裡,個人所減少的負擔就這麼加重在其他人的身上。把物品在正確的時間點放到它該存在的地方,同樣是我教育小孩的信條。

每個元件如同個人一般把自己經手過的物件歸回到處理前的狀況,那個系統就會如同大環境一樣得以長久地良好存在。裡頭有任何一個元件沒有做好,就會有相對的小小影響;現在的硬體等級都很好,也許要經過很久才會出現問題,但因資源取得容易就認為可以隨便取用且亂丟終究是錯誤的行為。

保證每個元件處理好經手的資源,是維持系統穩定的必要條件;同樣地,人對於地球也應該抱持同樣的想法,才能讓生存的環境維持機能。至少,立即隨手把自己取用的物品放回原處,可以讓自己居住的場所保持整齊清潔,帶給身在其中的人較好的心情。

2007年7月26日 星期四

C14 View的設計(1)──起始與結束


這張圖是我所認定整個系統具有的完整層次。View的部分是以淡綠色所顯示的部分。

在架構設計的時期,第一步設計的對象是架構好上方所有的Interface,接著就思考每個部分的實作與對應使用的Component Interface,Component的實作則會在細部設計階段才需要處理。在這個階段,設計者應決定每一層Interface所應存放的位置,同時決定每一個功能在每一個Interface裡所對應的方法,以及方法裡應該作出的反應。

首先,在起動系統或是準備功能執行狀態的時候會有一個起動功能Controller負責產生顯示給使用者操作的畫面。畫面大多會以GUI(Graphic User Interface)呈現,裡頭必須包含功能所需之資料對應欄位(有些資料是經由計算或另外擷取的)。絕大多數的View只會有一種,這時可以省略掉Interface的定義;但是如果系統打算採用Multi-Channel的設計,就必須定義介面提供更換不同種類的View來存取畫面上的資料。

設計任何一個層次的時候,除了起動時自身的生成,同時也要注意功能完成後擁有物件的釋放。重覆不斷地使用資源卻沒有完全釋放的功能,會造成硬體的Memory Leakage而逐漸佔用系統記憶體,終至記憶體不足以宣告生成物件而導致系統停擺。在網路上所搜尋到的教學網頁全部都是教大家如何使用物件,還沒看到過同時教大家如何正確地完全釋放物件。結束時釋放的原則是以生成的方法順序倒過來逐一實行。

2007年7月25日 星期三

C13 小小的改變,大大的費時

公司的系統需要使用其他廠商的硬體來擴充功能,因而要我設計一個元件來對應這些功能。需求是在畫面上操作某些動作時,把資料變成XML字串送給其他系統,回應的結果再顯示到畫面上。

一開始沒想太多,只把送出的XML作成Model物件,Controller裡就直接在需要顯示的地方使用View的物件(公司的View經過包裝,使用自行開發的API),只用了幾天的時間很快地把功能做好並測試完成。過了幾天,主管說這個元件希望可以單獨銷售使用,View的部分希望可以同時支援標準的swing,為了做出這個功能我花了四個小時把Controller裡的View拆出並定義Interface。

直接把Controller與View分開並定義介面所花的時間應該只比原先直接使用的設計多一個小時,但是我花了整整四個小時做這個動作。而且在拆解的過程裡程式碼大量地被分割,連帶地所有功能全部要再重測與修正,總共用了六個小時左右,實在是得不償失。

類似的經驗在維護列印的元件上。當初寫這個功能的人看需求只要使用一款印表機,所以在列印的邏輯裡直接使用印表機物件;現在因專案的緣故必須支援三款不同的印表機,為了重覆使用印表邏輯,我必須花很大的功夫拆出邏輯與印表機功能物件並在其中定義介面。當然拆解後重新測試元件全部功能的動作還是免不了的。

當你多遇上幾次這種重拆重測的事,浪費許多時間重做之後,如果你是那個負責拆解與維護的人,相信你會更明白為每一個層次定義介面的重要性。

2007年7月24日 星期二

C12 應付變化的設計──Design Patterns

系統設計時常有需要變化的地方,累積了多次的開發經驗後,有人統計分析出一些較常見且需要的變化,並且設計出解決對應問題的機制,這些機制被稱為Design Patterns。

Design Patterns是很熱門的話題,搜尋一下網路可以找到許多相關的網頁。Design Patterns集結了前人的智慧解決系統設計上常見的需求,讓系統更具有抽換元件的彈性,但是增加彈性的代價是使用更多的Class來達成功能。因此也有Anti-Pattern的聲浪出現,希望設計時不要動不動就套用Design Patterns而造成設計上的負擔。

在應該變化的地方才使用Design Patterns是目前較中肯的使用時機,分析使用者所有需求的內容來決定哪些地方該使用Design Patterns。這樣的想法是合理的,如同記錄的內容需要平衡,設計的內容也同樣需要平衡;但是需求都可能變更,沒有人知道現在確定不變的事在未來會不會永遠不變。

我所採用的設計原則是:確定會變的地方套用Design Pattern,有可能會變但現在不變的地方定義Interface而不使用Class。設計時定義Interface再實作所花的時間大約是直接使用Class的三倍,但是把已經寫好的直接使用Class原件拆出Interface與實作差不多要花直接使用Class的九倍時間,而且還要另外再加上重新測試的時間。

就像記錄應該記下所有的事,但是內容應該簡單不要花太多時間,在設計的時候也應該為所有應該切割的地方定義介面預留未來的改變,但是不需要全部都套用Design Pattern。

2007年7月23日 星期一

C11 小朋友的親屬稱謂表

低年級的小朋友前陣子在苦背親屬稱謂表,一大堆叔、伯、姑、嫂、公、婆之類的名稱一下就讓小朋友頭昏腦脹,更何況還得去記得“爸爸的爸爸是爺爺“,”媽媽的姐姐是阿姨“等等這樣的人物關係。

教了幾種關係後,發現小朋友已經無法再多記,同時也感覺這樣的記法實在太耗費腦力;轉念一想,這個問題好像可以用OO的理念來解決耶。“人”肯定是獨立的個體,“稱謂”不正是從自己看出去後人與人之間關係總計後的結果嗎?只要定義好“人”這個類別,同時適當地決定與其他“人的關聯,那麼從關係求得”稱謂“是順理成章的。

“人”都有一位父親與一位母親(在這裡不討論例外情形)的長輩關係,同時有兄、弟、姐、妹四種同輩集合、子與女兩種晚輩集合、以及一位配偶關係。接下來從自己開始擺好課本所教的層數再拉上與人的對應關係,往長輩一層叫“父母層”、兩層是“公婆層”;父親的兄弟姐妹層的展開是伯、叔與姑姑,他們的配偶稱為伯母、叔母與姑丈;母親的兄弟姐妹層展開是舅與姨,他們的配偶稱為舅母與姨丈,依此類推……。

原先只能硬背的親屬關係在適當地切割獨立元件後,成為有條理可依循的關係圖表,相形之下學習者的可接受度與理解能力大幅地提升了。明明是相同的東西,經過設計與規畫再加以適量的說明,就可以把整個觀念傳遞給原先不清楚的人,這也是我們應該努力達成的方向。

2007年7月22日 星期日

C10 理論串接實作的瓶頸(4)──設計元件的層次

元件的設計就像政府機關處理流程的切割,系統的事件發生時應有一群元件來支援處理,每個元件有自己的特性,只做自己該做的處理。然而除了處理事件之外,每個單位都有自己的定位,規定了這個單位層級:有上下的垂直管轄單位與水平的同級單位。

系統的架構設計其實等同於政府機關單位的編排,我們必須根據事件的來決定該分給哪些元件處理,處理的同時也要決定元件所在的位置。原則上元件數量多時表示分工較精細,元件處理的內容會比較明確,但是會造成流程處理步驟數量的增多;數量少時流程處理較容易,但是元件的功能定義因處理的事件種類變多而相對地變得模糊。如何把元件正確地切割也是不容易做好的設計。

元件裡可能會使用其他元件的方法,這時他們處在垂直的關係;一個元件使用多個其他元件的方法時,那些被呼叫元件全部都處在水平的關係。元件之間的關係應該在決定使用時就決定其位置,包括與其他元件的合作關係。元件之間的關聯如果不固定,有時是垂直,有時是水平的話,會造成使用上的混淆;就如同人類的輩份般,該是長晚輩的關係就永遠是,該是同輩的關係也永遠是,如果同時具有的話,會被稱作“亂倫”。

系統的元件定位明確之後,如果同層級的元件已定義介面,那麼實作同層級介面的元件都可以任意抽換。想像一位老師需要三位同學來做某件事,“同學”在這裡就像介面一般,實際的物件是在班上就讀的“人”,三位同學的意思就是任何三位在班上就讀的人都行;在老師與同學之間設計介面就能夠隨意變更實際配合的人。

2007年7月21日 星期六

C09 做人的方法(2)──做應該做的

在現實生活的事件發生時,每個參與處理單位與人都有屬於自己的權責,對事件應有的全部反應都切分給各個單位分開處理。從事件角度來看,經過所有單位處理之後就完成了反應;從單位角度來看,就需要知道事件發生時屬於自己該處理的內容。

當我們去政府機關辦事時,通常會知道應有的處理步驟,然後依照流程一關一關地處理事情。處理步驟的每一關都有對應的單位對應,我們必須依序到各相關單位做屬於該單位應處理的步驟。表單在前一步驟處理後來到自己的單位,這時會先檢查表單內容是否已有之前單位處理後的印記與資料,接著做我們自己該做的事情,做完後再傳遞到下一個單位去。

在切割處理步驟的同時,會依處理步驟的特性交給適當的單位作處理。把全部項目的處理流程訂定妥當之後,每個單位會有所有處理事件裡在什麼時間點該做什麼事的總表,同時也在處理事項時清楚地定義出單位的特性;所有單位都只會處理符合自己特性的事。

在什麼時間做什麼樣的事,做屬於自己的事而不侵越他人的範圍,這也正是做人應遵循的原則。

2007年7月20日 星期五

C08 設計時由上往下

有些人在設計功能時,會在確認需求後就直接定義這個功能應該有哪些元件,應該有哪些方法。問題是,這個功能該有些什麼,不該有些什麼就只能靠個人的判斷嗎?在科學的領域裡,每一件事物的存在應該都有其道理,能夠根據道理來導出合理的結論。

設計應該遵循這樣的原則。功能實作的所有線索全都在System Requirement Specification裡,我們可以參照Activity Diagram中系統的反應動作來決定系統應該有的行為。在決定系統行為的同時,也要依據動作步驟的說明定義對應的功能元件。

根據元件設計的準則(高內聚、低耦合),把特性類似的動作交給同一種元件來處理,元件與元件之間利用介面呼叫以達到可抽換使用元件的目的,如此一步步架構出功能所必須使用到的元件。確認功能使用到的所有元件之後,還得決定元件要放在硬體的哪一層裡,同時對應著硬體間的溝通橋樑再設計傳遞的方式。

一個功能一個功能慢慢地設計,遇到類似的動作時提出為共用的元件,遇到差異的動作時規定如何判別走不同的路。靜態的架構漸漸浮現,動態的呼叫對應需求,日積月累之後就能夠完成系統的開發;當然,目前講的都只是通則,後面還有很多實際的工作在前頭等著我們。

2007年7月19日 星期四

C07 機器人的可置換手臂──介面的意義

男生在小時候應該都玩過機器人玩具,有的機器人的手臂是一體成型無法活動的,有的機器人的手臂關節可以旋轉,有的機器人甚至可以拔下手臂換裝其他類型的手臂。

在製造的技術上,一體成型的那種通常是直接灌模做出外型,生產速度很快但沒有任何變化;把手臂切成三節,加裝上可旋轉的關節後就可以變換動作;再者將手臂關節加以變化做成抽取方式時,前臂就能自由變換成手型、錐型、砲型、刀型等等各式不同的物件。

是的,可抽取式關節的意義就是介面。不管是什麼樣的手臂,只要接合端做成符合這種關節的介面,就“保證”能夠在這款機器人上使用,未來也可以根據這種介面再設計出更多不同類型的手臂。然而我們同時也發現,這種關節需要加工才會具有功能,與一體成型的方式比起來,得花費更多的時間才能夠享受擴充手臂的樂趣。

系統有沒有設計介面是完全不會影響功能結果的,注重結果的人通常會了省事都會忽略掉這一層而使得系統的變動性變得很低;期望在系統裡設計介面的人也必須在對的地方設計出介面才會有理想的效果。做,與怎麼做,才會讓系統有容易變化的彈性,是需要應該做的勇氣與經驗的累積的。

設計系統的道理就是如此,想要有擴充元件的樂趣,就必須額外花時間定義介面的內容。人生也同樣是這樣,想要多獲得什麼時,相對地一定得要多付出些什麼。

2007年7月18日 星期三

C06 外包給大學生做的程式

在2003年時,公司有一個專案需要大量的網頁來呈現後端的資料,那時因為人手不足的緣故而外包給五位大學生製作。開發完成後在整合測試時,我奉命支援那個專案添加一些功能,所以有機會看到他們所寫的JSP網頁程式。

他們製作的內容很單純,用html顯示內容再加上一些java script作控制;在控制的方法邏輯裡夾雜直接呼叫幾個通用處理與DB的類別;但是同事測試的時候幾乎是一邊測一邊念,因為一個功能的頁面與邏輯放在同一個檔案裡,類似的邏輯又重覆出現在許多功能中,測試的時候只要修改一個問題就得同時改變很多檔案裡相同的區塊。

這就是沒有把邏輯與顯示分開的典型,開發的時候把一堆東西夾雜在一起,在不用考慮關聯而直接使用的情形下,功能只要會動就好,開發速度超快;遇到第二個類似的功能,把第一個複製成第二個再修改差異部分的程式就完成另一個。我們看得到開發時的快速,但也看到必須修改時的痛苦,如果加上因特別因素非得拆開顯示與邏輯在不同檔案時,再投入的資源將會是良好規畫下所花費的五倍以上。

可是開發的人看不到這種痛苦,因為他們不是負責做這種事的人;專案經理也看不到這種痛苦,因為他們只管功能是否如期完成;老板也同樣看不到這種痛苦,因為他只感覺為什麼維護要花這麼多時間還做得不大好。這時負責維護的人要說什麼呢?只能祈禱能有明主可以解決這種痛苦吧。

2007年7月17日 星期二

C05 Robustness Analysis(MVC Pattern)

在開始功能設計時,我們可以直接依MVC的方式設定三個元件:即顯示的View、處理用的Controller、被處理的Model。我完全同意這應該是一個功能必須拆解最少數目的元件。

在需要顯示資訊給使用者時,系統需要一層View來顯示內容。這層的功能同時還要接收使用者操作產生的事件做對應的動作:有時是顯示狀態的改變,有時則是對資料內容的處理。在觸發的事件裡,應該會有一條路是去呼叫Controller來做事的。

作為處理入口的Controller一開始只有一個,在這裡我們應該先對應Activity Diagram來思考系統應該有哪些對應動作,同時先在註解裡用文字簡短描述每一個步驟,待專案人員review過邏輯的正確性後再著手設計。屆時就依步驟思考,是否需要呼叫其他Use Case的Controller來共同達成功能;這是必須參考需求階段的產出來判斷的。

Controller裡總會有一個(或一個以上)的步驟是改變資料模組的,在此要決定這個功能要處理的資料模組是哪些,為Controller與Model拉起關聯。Model這層的資料模組,在比較複雜的處理情況時還必須包含存取的邏輯在內。

實作的時候,我們會在Logical View裡先做出一個Use Case Realization的圖示,再加上一張Class Diagram,把代表MVC的三個class加到圖裡,並先拉起初步的使用關聯。

2007年7月16日 星期一

C04 做事的方法(5)──結果論或過程論?

舉凡做事,都會有過程,有結果。不管是歷史或身邊所遇到的事都是以成敗來論英雄,因此很多人都同樣的以這種觀點來看待所有的事。一次性的事件由於永遠不會再重來,所以留在世人心中的只是結果,過程只是拿來檢討當初會成功或失敗的關鍵點而已。但是系統呢?它可是會一直重覆行動、甚至在做過改變後再持續行為的。

如果只看結果,那麼系統如何達成功能的過程幾乎都會被忽略,不管使用或維護都感覺像是一個無法明白內部機制的黑箱,我們只能知道結果對或不對。在偏重結果的情況下,內部的設計很可能就是幾個難以分解與切割的大型元件,在未來產生設計變更時,運氣不好的維護人員就得花上原先有作拆解的數倍時間來達到新功能的需求。

注重過程的作法會把理論與經驗裡所知道應該抽離的部分放置在不同的元件,讓各元件具有其自己的責任與意義,在元件完成自己責任時呼叫所有相關的元件一同完成指定的功能。由於系統功能會被不停地測試,我們可以依據測試出來的結果來判斷可能在哪一段的處理發生問題,這在只注重結果的方式裡是很難理解的。未來遇到需求變更時,我們應能知道應該要抽換哪一段處理元件或是擴充其功能來新增參數;這種便利性在黑箱作業裡是很難出現的。

系統的過程,如果能將元件分割成類似組裝合成的產品,一件件地加以拆解在不同地方生產或是取得現成可用的,這不是很理想嗎?但可惜的是對絕大多數的人來說,軟體工廠的實現卻有如難以實現的夢。

平時做事即使全用結果論來看待事情成敗都還可以,但是從事教育時絕對要記得使用過程論。一次定成敗的結果論對於初學者來說實在是過於嚴苛的考驗,教育是讓學習的人經由一次次地檢討過程中的錯誤,並於一次次的演練中逐漸明白過程如何處理才會有最好的結果,進而演變為學習者本身的能力。

2007年7月15日 星期日

C03 銜接硬體的橋樑

硬體與硬體之間的連結,是要決定把資料從一台傳到另外一台的方法與通訊協定。選擇的決策會根據客戶的指定或是技術的考量來作出最適當的選擇。

Client to Server:以Http Connection連接時傳送的大多是html或xml,以rmi連接時傳送的就會是object。

Server to Database:通常用jdbc來實作,會傳送Statement,需要回傳物件時是ResultSet。

其他系統:會由該系統決定連線的方式,Web Service、MQ、Database、檔案都會是可能的選擇。

決定硬體之後,要在Deployment View裡畫上所需的硬體並標記每個硬體間連接的方式,每項硬體的規格與必要安裝的基本軟體(像作業系統等級、資料庫軟體等等)可以註記在備註欄裡。硬體的決定是很重要的階段,大部分的功能都會經過這些硬體的橋樑,如有變更影響都很大,最不幸的時候必須得重新設計。


雖然到這裡只接觸了資料模組與硬體架構的設計,但是經由抽取兩者相同的部分,我們發現設計的根本是基本三部曲:決定物件的佈置、決定物件的連接、決定傳送的內容。這項通則,無論在資料模組、架構設計、細部設計或是類別的實作都能夠適用。



◎系統的目標

2007年7月14日 星期六

C02 依需求決定硬體架構

蓋大廈時需要先挖好地基,然後用鋼筋建立骨架;開發系統同樣要先確定資料的儲存方式,接下來再建立系統的架構。系統的架構會根據硬體的布置而決定。常見的硬體架構大到有以下幾種:

第一種是單機系統。系統在單一電腦上運行,資料以檔案方式儲存在硬碟裡;有些系統只提供運行,完全不儲存資料。

第二種是Client-Server架構。系統在Client個別執行後,資料統一集中到Server處理後再分別傳回到Client。

第三種是3-tier架構。是Client-Server再加上Database所組成的三層式架構。

其他更複雜的就被稱為n-tier架構。比如在3-tier架構外還得與Host連線,前端支援Client種類不只一種,有些server在防火牆後之類的,都屬於複雜的n-tier架構。

從客戶的需求訪談裡決定系統硬體架構的類型是進行設計前重要的準備關鍵,要以應付現在需要為前提,同時要能解決未來成長的需求。因此,架構決定的同時,為每一部電腦決定符合前面要求的硬體等級也是必須小心從事的。

在硬體架構的設計時,即使目前客戶的需求只是固定一種類型,假使開發的系統預期在未來可以重覆使用的話,在接下來的設計就要考慮應用在不同架構時的情況。

2007年7月13日 星期五

C01 工程的設計圖

許多程式設計人員都認為系統只要執行正確,同時讓使用者知道怎麼操作即可;也因而造成設計時直接把想法變成程式,完全沒有將想法與經驗留下文字的記錄,進而造成其他接觸該系統的人只知道系統的反應現象而不知道為什麼得這樣反應?原因是什麼?

想像自己剛買一部新汽車,開了一陣子後感覺油路不順進廠檢修,維修人員說這個現象很可能是火星塞的問題,應該更換新的就好;可是他接著說不確定火星塞在哪裡,必須要把車子拆開來找才能確認。這時,你會不會吐血?再者,造物主創造出人類,人類很快地明白自己可以做出的動作並在有問題時作簡單的治療,但是我們用了多少時間在找出本身的構造、功能出問題的原因與食物對人的影響呢?

如果維修人員曾經看過汽車的構造設計,他可以立即知道火星塞的正確位置;如果創造人類時有留下結構圖、不同輸入時的影響與有狀況時的問題排除手冊,人類需要耗費這麼多時間去找出原始創造者的想法嗎?設計圖除了作為施工時的準則外,也是完成後除錯或增加功能時最有力的參考依據。

開發系統的人也常常直接做好系統後才撰寫系統開發的回憶錄,雖然同樣產出設計文件,但是這只是為了產出而產出。除了回憶出來的東西必然與實際系統有很大差距之外,文件裡也不可能正確註明某個功能的實現是使用了哪些元件與哪些方法的追溯,更不可能明白類別與方法是為了什麼原因而存在。與想法不能同步的文件,是白費功夫做出來也沒有人想看的產物。

2007年7月12日 星期四

B24 程式執行時直接操作資料

2007年初時部門進來三位新人由我先給予基礎的訓練。某一天我安排了讓他們寫一個程式讀取XML檔案的內容並依序把每個Node名稱一行,每個Attribute的name與value都放一行集中到最後儲存為一個文字檔。

果然如同預期那樣,第一次使用XML DOM的人都認為這已經把檔案內容物件化,因而在程式的處理過程中直接操作;輸出的部分則使用一個StringBuffer收集所有的輸出後一次把字串寫回檔案中;而且所有的程式都集中在一個Class裡。以這次提出的程式需求來說,全部的人都成功做出符合功能的程式。

接著我對他們說,現在客戶的資料因為系統升級改成從純文字檔輸入,你們要如何改變程式?再來,讀到的內容在轉出為文字檔前,客戶希望可以出現一個Table逐筆顯示所有的內容確認後再行存檔,這又要怎麼改呢?以現在的設計方式,會不會改到死?他們想了想後全都點頭。

Data Model的存在就是在預防未來資料的存取方式改變時,不會造成資料物件的改變進而影響其他使用資料的程式跟著要變更。我也不大贊同使用SQL代號找出SQL Command來存取資料庫而把資料放在兩維陣列物件的方式,雖然在開發時可以有較快的開發速度也較容易測試,可是在資料庫定義有改變的時候沒有Data Model居中緩衝,結果造成需要改變的功能必須另外做出一套類似機制的結果。

為了開發時的快速,面對重大的改變時無法將差異融入到先前的設計而必須另外拉出成一套機制,其至更多。如果設計的人面對這樣的情況時,會有想從一開始就改變的想法嗎?

2007年7月11日 星期三

B23 決定資料模組的儲存方式

資料模組的運用不外乎兩個方式:對外部系統的傳送與接收或自行管理資料的儲存與讀取。意義在於當系統關閉,記憶體內的資料完全消失後,還能從特定的地方獲得某個時間點的運行資訊並予以復原系統之狀態。

資料儲存的方式有兩個思考的重點。首先是方法,系統設計之初就要決定資料存取的方式,我們必須依照需求提到的內容判斷資料應用是傳給外部系統還是自行管理。傳送時要考慮傳送的方式、通訊協定等;自行管理時要考慮儲存的環境、方法等等。

再來是格式,儲存的資料要以什麼樣的格式放到儲存的地方,要如何讀回並復原成資料模組的物件是考慮的重點。不同的儲存方法會有不同的定義格式,一個系統也可能要對應多種不同的儲存方法;像銀行人員使用的系統幾乎都需要與主機通訊、存取資料庫、存取檔案,有時還要允許從網路連線操作系統、使用Web Service等等。

資料是系統內所有功能都需要操作的對象,也就是屬於最底層的設計。如果資料模組做得不好,在系統開發到一定程度時才發現需要修改,那麼就需要跟著修改一大串使用資料的程式。根基需要打好,才有法子令系統順序架在上面開發;“查埔人身體哪顧厚勇,家庭自然就會幸福”講的正是這樣的道理呢。

資料模組的設計,絕對不能忘記未來的擴充性。


◎系統的目標

2007年7月10日 星期二

B22 資料模組(Data Model)的建立

系統詞彙表建立好之後,我們可以應用CRC Card的方式來篩選所有名詞之間的關聯來決定其從屬關係。CRC這三個字母代表的意義為:Class, Responsibility, Collaboration,也就是本身、責任與合作,恰好可以對應到是(is)、擁有(has)、使用(use)這三個物件的關聯。

在這個步驟,我們將應用CRC Card方法從需求文件裡找出適合作為資料物件的名詞並命名(Class),並進而找出該物件必須記得的所有特性(Responsibility),以及自己不需要記得但是在特定時候會使用到的其他物件(Collaboration)。記錄下這些物件的內容與關聯,就會形成系統所需要的Data Model。

在現實生活裡與一個物件的相關的特性及其他物件實在太多了,但是我們只需著眼於系統所需要的特性。比如說一個人會的程式語言與頭髮顏色都屬於那個人的特性,但是今天公司的職缺需要會某種程式的人,那麼在徵人的需求裡就不會使用到頭髮顏色,人這個class就只需要記得程式語言即可。

收集好的Data Model最後還要進行最佳化的動作,比如消除多對多的關係,消除關聯物件等等的步驟。大多數的系統資料都會儲存在資料庫裡,最佳化後的系統Data Model可以作為資料庫表格設計的最佳參考資料;即使資料儲存在檔案裡,也可以依設計把資料內容分開存放,便利於管理。

2007年7月9日 星期一

B21 做人的方法(1)──記得該記的

物件的關聯通常有三個類型:是(is)、擁有(has)、使用(use)。

是(is):定義出物件本身的種類。當一個男人說出“我是男人”時表示他即使做出一個男人應有的行徑;高志航在電影裡說“身為中華民國空軍,怎能讓敵人的飛機在我的頭上飛”時表示他將做出符合中華民國空軍的行為。是(is)在OO裡表達的是extends,描述物件具有父物件的全部特性與行為。

擁有(has):定義出物件內部所有的特性。車子的顏色、油箱容量、乘坐人數、出廠年份等等的描述,都是車子這個物件的特性。特性也是名詞,只是它們附屬於車子這個名詞之內。擁有(has)在OO裡表達的是物件之內必須管理的其他物件。

使用(use):定義物件在動作之中使用到不屬於自己的其他物件。如果你要從台北搭車往台中,執行這個動作的車通常是別人的,屬於別人的物件只有在該項行為裡才會暫時使用,自己並不需要持續記得別人的物件;要注意的是,如果要使用自己的車則需先取得自己的車再讓該行為使用自己的車子。使用(use)是描述其他物件只有在物件執行特定的行為時才會使用的這種關係。

物件屬於什麼樣的類型就應具有該類型一切定義特性與行為,記得自己應該記得的特性內容,記得做某些行為時需要使用哪些別的物件,視需要的狀況把其他物件記下來。每個物件都記下自己應該記得的物件,每個物件也被該記得的物件記著;每有物件各守其份時,任何物件的存在就都可以被追蹤出來並取得。

2007年7月8日 星期日

B20 建立系統詞彙表

既然Model是獨立的資料,系統又必須依據Model來設計功能。那我們要怎樣找出適合系統使用的Model呢?

現在我們已經收集並分析過需求的資料,並將功能需求存放在SRS裡。Model既然是靜態的,那表示我們可以從靜態的名詞取得類似的意義。因此在需求收集與分析的同時就應該開始收集需求裡使用到的名詞,來源主要會出自於Use Case描述與Use Case Scenario。

收集下來的名詞同樣需要分析,明顯與系統無關的名詞則予以捨棄,留下的每個名詞都需要用文字說明其意義。名詞同義字如有多個時必須選一個作為代表以避免未來使用時發生混淆。詞彙表主要是用來定義系統最基本的資料模組,同時也限定在溝通與文件上使用名詞都統一依照定義。

在收集需求的同時,系統詞彙表的內容就陸續依照需求中出現的名詞加以擴編並整理。等到需求分析接近一個段落(像訪談完一個子系統就可以)時,就可以開始著手設計系統的資料模組。

2007年7月7日 星期六

B19 執行系統是為了處理資料

電腦應用程式無法計算有多少類型,但是終歸一句話:程式的目的是用來處理使用者想要操作的資料。

舉例來說,使用Word的目的是要產生排版的文件,使用PowerPoint是為了製作投影片,PhotoShop是為了讓使用者做出漂亮的圖檔,線上購物是讓使用者找到喜歡的物品並下單購買。文件、投影片、圖檔、商品、訂單,這些靜態的物件都是資料。

MVC的概念目前廣被接受。靜態的處理資料稱作Model,將靜態資料以各種適當的方式呈現在使用者面前並接受使用者改變的是View,依照使用者的想法取得Model呈現在View、把View上使用者的變動回存到Model的責任則落在Controller的身上。Model-View-Controller的概念讓系統的設計有了分層分工的想法。

在責任上,View是用來顯示Model內容,Controller用來存取Model的內容,在使用的關係上來說View與Controller都必須有Model存在之後才能知道自己要如何操作Model。Model的來源是存在於系統外的,它應該是獨立且不受其他因素影響的,通常存在於檔案、資料庫或其他系統裡;系統只是為了使用Model而開發出來的。

2007年7月6日 星期五

B18 需求階段的模型(Model)與應產出文件

在需求階段裡,藉由訪談客戶獲取系統需求加以分析並記錄。記錄下來的物件關係模型如下面附圖。圖中物件間的關係如下:與A19的基本概念模型比較,會發現他們的本質上是相同的。

◎系統由一個或一個以上的Use Case組合而成
◎一個Use Case由一個或一個以上的Activity組合而成
◎每個Use Case有一個或一個以上的Actor
◎每個Use Case有可能影響外部系統(可能沒有)
◎每個Use Case都應該定義該Use Case的例外狀況(可能沒有)
◎每個例外狀況都要有一個或一個以上以上可能的其他Use Case
◎一個其他Use Case由一個或一個以上的Activity組合而成


在這裡主要的產出是Use Case的說明文件,也就是SRS,同時包含Activity Diagram與每個Activity的簡述。但是系統應該有一份Use Case List來核對系統限制的範圍,如果必要的話每個Actor(包含外部系統)也該有一份描述文件。另外,以Use Case為主的追溯資料也應該產生:像Use Case vs. Use Case、Use Case vs. Activity、Use Case vs. Actor等雙向的追溯表(在這裡應有五份)。

需要產生的文件雖然有很多份,幸運的是大多都只要在模型中建立再依據SoDA範本產生就可以應付。真的需要花心力輸入的主要還是SRS的資料。

2007年7月5日 星期四

B17 功能需求(4)──System Requirement Specification

無論在哪一個物件上,都會有靜態的描述與動態的行為存在,即使是Use Case也不例外;而SRS的內容大致上就由這兩類的資料所組成。

靜態的描述內容通常有:Use Case編號、名稱、描述、操作的Actor等,如果Use Case有特定的Nonfunctional Requirement需要達成也要註記在備註裡。(記得:NFR有哪些Use Case符合必須另外收集起來)與這個Use Case有關的客戶文件也必須裝訂在一起。

動態的行為內容通常有:進入流程前的系統狀態、啟動這個Use Case的條件、Use Case Scenario、Activity Diagram與Use Case結束時的系統狀態(如果有多個,都必須記錄如何操作會導致不同的結果)。在這個段落也應該註明自己被哪些Use Case所使用,同時又使用了哪些Use Case。

System Requirement Specification是需求分析後最重要的產出。之前所有收集的資料與分析的結果都會記錄在這裡,接下來無論是客戶確認、設計、實作、測試或製作文件,都會以這份資料作為延伸的標準。

功能需求是系統的組成單位,每個需求裡都描述清楚自己本身的特徵,同時記錄與其他功能需求之間的互動與關聯。良好分析之後的需求規格,會有比較高的機會導出擁有良好結構的系統。


◎系統的目標

2007年7月4日 星期三

B16 理論串接實作的瓶頸(3)──從Activity Diagram再refine Use Case

Activity Diagram只有流程圖的意義嗎?如果僅是這樣的話,那就不用浪費時間畫圖了吧。

在製作Activity Diagram的時候,除了Activity的順序與swim lane之外,我們還必須決定Activity的分類。原則上Activity屬於現在製作的Use Case,但是如果某個或某些特定順序的Activity已經出現在別的Use Case裡時,這表示這些Activity有reuse的現象發生。此時應該進行抽出共同部分的動作,意即把這些Activity另行放到一個新的Use Case裡,再讓現在的Use Case去參照拉出Use Case裡的Activity。

很多人都沒有發現這層意義,在表面上誤以為Activity Diagram只是表達流程概念,用漂亮的流程圖工具為每個Use Case畫了一張Activity Diagram後交差。由於UML定義的是表達想法的模型語言,大多數的人就只拘泥在表相的Language,而忘記Model才是它最深層的含意。

為每個Use Case表達流程概念、區分每個Activity所屬的swim lane、拉出共用的Activity為特定功能Use Case,經由表達、分析與提取,慢慢地找出隱含的關聯並為系統建立適當而完善的Use Case List,這是Activity Diagram在這裡的重要性。

一般人通常的想法是,因為系統要這個功能,所以這裡應該有什麼什麼才對;可是某甲認為該切成三個,某乙說應該有五個,這時要怎麼辦才好?如果沒法用科學化的方式分析出有幾個比較適當,那麼開發人員的想法會因為各自的主觀而難以同步。

2007年7月3日 星期二

B15 理論串接實作的瓶頸(2)──Activity Diagram的意義

Activity Diagram是用來說明Use Case內的動態流程,使用者操作步驟、系統當下該有的反應、與外部系統的互動都該在這份圖裡呈現。Activity的意義幾乎等於流程圖,但是如果把Activity Diagram畫得像流程圖,就極為可能造成系統界限與動作責任畫分不清楚的風險。

Activity Diagram裡有個叫swim lane的有用元件,我們可以使用它來基本地切分使用者動作、系統反應與外部系統的責任區塊。製作Activity時同樣像畫流程圖那樣依序加入,但加入時務必決定好該Activity在哪一塊區域內發生的。使用者作了什麼操作後進入系統處理,系統在內部如何處理、有什麼改變,改變之後如何與Actor互動,在這裡都依照發生的地方加以區隔開來。

這樣做有什麼好處呢?流程不是完全都一樣嗎?從整個操作順序與功能目的來看是沒什麼不同,但是從swim lane的權責角度來看就有很大的差異。使用者區塊代表的是人的動作,我們可以依區塊內的順序撰寫測試個案以及驗收時要交付的操作手冊;系統區塊代表的是需要開發的動作,在使用者的什麼動作後該有什麼樣的反應、做什麼樣的事是未來設計的依據;外部系統區塊代表的是系統與其他系統的互動,可以知道在什麼時間點要與外部系統有什麼樣的關聯,回傳的結果要如何處理等等。

把所有Activity放在所屬的swim lane對於系統開發的影響是非常大的,如果沒有注意到這個地方會使得動作無法明確區分而造成設計時的混亂。同樣地,在購買OOAD相關書籍時只要翻開Activity Diagram的範例,用有沒有swim lane的存在來得知作者有沒有過成功的實作經歷。

2007年7月2日 星期一

B14 Rational Tool(3)──Activity Diagram

Use Case Scenario應被畫成Activity Diagram。在製作的時候,要依使用者操作的順序,將屬於User的Activity依序往下排列。依照流程把Activity用箭號連接起來;每個需要系統反應的使用者操作,另在User Activity的右邊拉出一個屬於系統的Activity並串接起來。系統Activity結束時再接到下一個User Activit上直到結束。

所有的Activity都只用簡短的描述作為名稱,詳細說明輸入在註解欄裡。需要判斷的地方加上菱形並對流出的箭頭加上Guard Condition說明。直接畫Activity Diagram的話,其實與用Visio畫或者是用Word描述操作步驟有什麼不同,但是使用Rose的好處是我們在製作Activity Diagram的同時,可以作Activity的分類,將之歸納在它應屬於的Use Case裡,同時可以在任何時候從其他Use Case裡拉入該Activity。

Reuse Activity的好處在於我們對Activity的名稱有變動,或者再次分析發現必須移動所屬的Use Case時,可以直接動作並同時變更所有使用它的Activity Diagram而不必一一變動。用SoDA同樣可以自動在報表裡產生圖表與每一個Activity的說明文字。

在分類Activity與使用其它Use Case裡的Activity時,我們能夠更精確地判斷Use Case之間存在的關聯;同時在共用某些Activity次數多的時候,更易於決定要不要把那些Activity拉出成為獨立的Use Case。

2007年7月1日 星期日

B13 功能需求(3)──Use Case Scenario

記得嗎?工作項目是由一個以上的工作步驟組成的,Use Case也是由一個以上的操作步驟組成,這些有順序的操作步驟被稱Use Case Scenario。

Scenario是使用者達成該功能的操作順序,裡面記載了使用者與系統之間的互動與影響。Scenario必定會記錄所有可以正常完成的結果,有時也會另外記載一些錯誤時的系統反應。根據Scenario的說明我們可以得到使用者在該Use Case裡的操作順序,要保證系統正常的最低程度應該是依照Scenario裡的步驟去做都會有預期的結果;系統反應的方面可以作為設計與開發系統的依據,使用者操作順序可以用來驗證系統動作是否正常(意即測試),並且成為驗收時操作手冊的根本。

在訪談Use Case時就必須針對顯示的畫面、操作的動作、系統的反應與輸出的結果等細節加以確認,說明資料取得越詳盡越好,最好可以得到客戶的簽名確認。要客戶確認大部分的功能在專案的環境裡是比登天還難的,他們常常想到就會來改一下,所以應付這類突如其來的需求變更,系統必須要設計為易於開發且具有彈性才行。

在我看來Scenario還有個更重要的地方是在於,客戶會根據操作劇本的內容來驗收系統功能。正因為向上對應驗收條件,向下對應設計與測試,所以確認Use Case Scenario可以說是開發系統最值得注意的階段。