This page looks plain and unstyled because you're using a non-standard compliant browser. To see it in its best form, please visit upgrade to a browser that supports web standards. It's free and painless.

Kenming's 軟體設計思維 會員登入 會員註冊

前言

要能建構一份有效的使用案例需求模型 (Use Case Model),比想像中得難,使用案例是 “易學難精”,只是那麼幾個簡單的圖形元素來畫使用案例圖、以及僅用白話的文字敘述來紀錄使用案例的需求描述,但是,若沒有把持使用案例最根本的原則與精神,真的很難寫得好。筆者在諸多 IT 單位的顧問輔導經驗中,所看到多個單位所 “畫” 出的使用案例,10 個中就有8個有問題,探究其中最大的問題,在於寫使用案例的需求分析人員,很難擺脫掉 “既有傳統” 的束縛,例如會從企業流程 (Business Process) 的觀點來分析使用案例,或者是馬上就聯想到系統內部的實作問題,包括權限控管等問題。筆者常開玩笑的說,要能寫好使用案例,最好本身就是 “無知”,不知道企業流程的邏輯判斷規則、”看不到” 系統內部是如何實現(Realize)使用案例的。奇怪的是,要能 “逼” 自己 “無知”,對系統需求分析人員好像很難,而且往往就是在不知不覺中,就把既往對 “領域” 與 “平台” 的知識給表達在使用案例中,事實上,根本就是把使用案例當成是 “另一種” 需求記錄的工具而已。

很是可惜,筆者一直認為,使用案例絕對是以 “專案型態(Project-based)” 開發的最有效利器,它表達的就是 “純粹” 的資訊系統 “局部” 功能觀點的需求模型,只要能寫好使用案例,甚至在不需要作系統內部結構分析的情況下,馬上就可以轉移至程式碼的實作階段,而且是很快速直覺。再利用分析層次的類別 (analysis class),就可以建構符合三層 (3-tier) 結構的 MVC 模型,有效隔離 UI 與資料庫的變動性,同時,再加上一個極為重要的手段:
”對每一個使用案例”,都加上能驗證功能的測試程式碼!” 如此在每一次的功能需求變動,都能提醒開發人員注意變動的影響部分,包括功能測試碼也跟著要修正。

為了順應短線的專案開發生態,快速實現「使用案例」,馬上導出至系統的實作,是最有效與最現實的手段。然後加上兩個配套的措施,以確保專案的品質:以使用案例為單位,撰寫功能測試程式碼;利用分析類別,建構符合 MVC 的三層實體框架,以隔離 UI 與 資料庫的變動。

而當第一個釋出(release)的專案能滿足系統的功能需求後,因此而能取得更多的開發資源,以及開發人員的溝通默契與技能逐漸成熟後,且已有基本的框架結構,此時再針對程式碼施以重構(refactoring),以及對結構作重整(refine),兩者是屬於系統內部結構分析的範疇,如此而更有機會成為可再利用性高價值的產品(Product)。

所以,如何寫出有效能確實表達系統功能觀點的使用案例,可以說是專案開發成功與否最重要的關鍵。但如同前述,使用案例是「易學難精」,在此筆者列出幾個比較常見的問題,再以簡單的三言兩語來說明為何這些是問題、SA 大概是以什麼角度、假設點在分析使用案例的,然後筆者再提供修正後的建議。底下的每一個問題,是經過簡化,實際的案例,大部分是同時有著眾多下述的問題合著來的,而開發團隊卻經常有茫點,而無法理出問題會是在哪裡。

1、從企業流程的觀點描述使用案例之間的關係

問題:這幾乎是因為太清楚領域知識與業務流程的 “資深 SA” 所畫出來的,經常都會把使用案例當成是企業流程的活動(activity),然後 “想辦法” 將這些使用案例依據業務流程的規則與順序給 “串起來”。請記得!! 使用案例是不表達業務流程的,業務流程的表達,會利用如活動圖(activity diagram)來描述,使用案例僅僅是表達參與者在某個時間點使用系統的局部功能觀點。

問題—從企業流程的觀點描述使用案例之間的關係
圖1、問題—從企業流程的觀點描述使用案例之間的關係

修正:
絕對不要把業務流程的觀點給表達在使用案例的,就是老老實實,根據參與者希望系統該提供 “哪些系統功能”,這些功能,就會成為一個個的使用案例。

修正—只 “老老實實” 表達局部功能觀點的使用案例
圖2、修正—只 “老老實實” 表達局部功能觀點的使用案例

2、模組化的分析思維

問題:
這也是分析使用案例相當常見的老問題。SA 將以往模組化(modular),也就是以功能樹(functional tree) 的分析思維,將大功能逐層分解成一個個的小功能,然後只是利用使用案例來取代傳統的模組化的表達而已。

問題—利用使用案例來表達模組化的分析思維
圖3、問題—利用使用案例來表達模組化的分析思維

修正:
模組太大了,無法成為有效、以使用案例為單位的功能點(functional point)。使用案例的單位要能符合 “S.M.A.R.T” 原則,也就是 " Specific(具體的)”、”Measurable(可測量的)”、” Accurate(精確的)”、”Reachable(可達成的)”、"Time-limit(有時限的)”。更簡單的說,每一個使用案例都是可以個別在某個時間點內,系統能履行功能來滿足參與者使用系統的目的。

另外一點要注意的是,圖 3 SA 不一定會畫出 “主檔維護作業” 這個大模組的使用案例,但是,若將使用案例命名為如 “人事主檔作業”、”出差勤作業” 等,SA 的心中,八九不離十,仍是模組化的思維。使用案例的命名法則,一定是 “動詞+名詞”,充分能表達出系統 “What(goal) can I(system) do for you(actor)” 的目標。所以使用案例的命名,一定會是 “維護人事資訊”、”維護出差勤資訊” 等。這好像是細節? 但其實往往命名就能反應出 SA 的想法、假設點等分析思維的。

修正—符合 S.M.A.R.T 原則的使用案例
圖4、修正—符合 S.M.A.R.T 原則的使用案例

3、權限檢核的 “How-to” 思維

問題:
這幾乎是 “Coding 人員” 兼當 SA 的作品。在分析使用案例時,馬上就聯想到系統的實作,包括權限控管、交易控管、企業邏輯等條件判斷這些諸多實作面 “How-to” 的問題。

問題—分析使用案例時,馬上聯想到實作面的議題
圖 5、問題—分析使用案例時,馬上聯想到實作面的議題

修正:
資訊系統層級的使用案例會以 “黑箱(black-box)” 來看待系統,使用案例的敘述只專注在參與者與系統之間的互動對話過程(session),系統的實作,會是留待在系統內部的結構分析與設計上的階段,例如,在循序圖的設計階段時,才會來描述實作面上所關注的議題,如本題 “權限檢核”、或者 “2-phase commit” 交易控管的問題。事實上,”誰(actor)” 來使用該使用案例,就多少已經隱含了權限控管的考量了,例如,行政人員可以 “維護員工資訊”,但員工卻沒有權限來使用該使用案例。

修正—不要在使用案例中表達系統實作面的設計
圖 6、修正—不要在使用案例中表達系統實作面的設計


4、系統本身當參與者(Actor)

問題:
當 SA 發現到系統需要在某個時間點,要能 “自動” 執行某個使用案例時,會以為系統本身就是參與者。ERP 的系統作業,例如日結帳、月結算、批次處理等相關作業,相當常見到這類型的案例。

問題—開發的系統,本身又當成呼叫自己的參與者
圖 7、問題—開發的系統,本身又當成呼叫自己的參與者


修正:
回到 “參與者(actor)” 的最基本定義,參與者只有兩種,一個是使用系統的使用者,另一個是外部的系統,所以,在開發範圍中的系統,不可能自己又是擔任外部的參與者角色,會 ”自動” 觸發使用案例的參與者,其實是外部系統,例如 “Timer”、”排程系統(schedule system)” 等。請注意,在分析使用案例時,最好能將系統範圍(system boundary)界定並表達出來,如下圖,就很清楚開發的系統是財會系統,如此也比較能避免掉 SA 不知不覺就把開發的系統又當成是呼叫自己的參與者這樣的謬誤。

修正—界定系統設計範圍,確實釐出系統的參與者
圖 8、修正—界定系統設計範圍,確實釐出系統的參與者


5、子系統(sub-system)的定義不明確

問題:
下圖乍看之下沒有問題,但筆者一定會就此圖問 SA 與系統架構的開發人員,是否未來在 ERP 系統的開發,有確實將「財務管理系統」分離成為獨立的一個模組(Module),也就是說,ERP 系統必然只能透過介面(interface)來呼叫「財務管理子系統」所提供的 APIs(Application Programming Interfaces),而不能直接連接該子系統的私有資料庫。但幾乎所得到的答案,都是沒有所謂的介面設計的觀念。將「財務管理」規劃為子系統是因為業務面的需要,是依照功能來規劃 ERP 會有哪幾個子系統(或者稱之為模組),但那僅此於業務面的邏輯區分,在實體的資訊系統中,並沒有界定這些子系統(或模組)之間,是透過介面的訊息溝通傳遞。

問題—子系統的定義不明確
圖 9、問題—子系統的定義不明確

修正:
既然在實體上,並沒有確實抽離「財務管理系統」,那麼,系統的開發範圍就必然是在 ERP 單一一個系統的範疇之內,也就是沒有「財務管理」這個外部的系統參與者;同樣地,也沒有所謂的「銷售子系統」來呼叫 ERP 系統(透過介面的呼叫),以下圖的使用案例為例,參與者其實是排程系統。

修正—業務邏輯面的子系統,並不能成為 ERP 系統的外部參與者
圖 10、修正—業務邏輯面的子系統,並不能成為 ERP 系統的外部參與者

這兩個月,我們顧問團隊,還可真是相當地忙,從 10 月到現在(包括到年底),幾乎每天都要出去,除了對企業的軟體設計教育訓練、IT 資訊單位專案開發的顧問輔導,還有幾個正在洽談的專案輔導案 ...,喔,還有演講與座談會等。嗯,看來「軟體設計顧問」這事業,在我們手上,慢慢已有逐漸紮根,也有許多軟體公司與 IT 單位等越來越比較能認同我們的軟體設計理念,以及所帶給他們所謂的 "最佳實務" 的方式。

以上星期而言,我與 Ringle 竟然一起跑了五個單位,最有意思的是,每個單位的需求範疇與實現的平台都完全不一樣。嘿,這對我而言,實在是樂趣多多,客戶所牽及問題的廣度與深度,讓我得以從各種不同的觀點與角度來思考與回應。我是不太擔心會被 "問倒",反而是有些擔心客戶不太敢提問問題,有時反而還要我主動來 "引導" 客戶來回應與互動討論。

因為上星期所發生的一些事件實在很有趣也有意思,甚至,還蠻有成就感的,所以還蠻想分享個人的心得與樂趣,藉此也說明,我,以及 Ringle,都很確定一件事: 軟體設計真的很有樂趣,解決問題(問題越大越複雜越好),是一種享受,而不是折磨。

星期一,是固定到國內某大零售超商的資訊公司,擔任某一個專案的顧問輔導。該專案在我們加入前,據他們說已跑了兩個月,仍然在需求面的分析還沒 "定案",所以無法轉到 "Coding"。嗯,這已是普遍軟體公司專案開發的 "常態",我們是從不信這一套,所以輔導一開始就重新 Review 與決定架構與開發設計範圍,然後找一個大 BP(Business Process),逐漸收斂,以活動圖(Activity Diagram)描述與記錄後,再協助他們依據我們所給的一個簡單 "mapping" 的原則,就可轉出到對資訊系統開發最有效的使用案例 (Use Case);再來是打開一到兩個的使用案例,利用基本分析類別(analysis class)的框架,利用循序圖來描述出內部結構元素的互動情形;然後... 第四個輔導日就給產出實現使用案例的應用程式碼,還包含了測試案例與功能測試碼等。整個輔導的過程,是還同時負責這些相關設計產出的教育訓練。我們是蠻主張的,從一個小型的實戰專案中,藉由有經驗的顧問,擔任教練的角色,協同作戰,快速跑過整個開發循環,然後從過程中釐出團隊成員彼此間看法與想法的分歧、再慢慢去作 "調和" 的工作,快速開發、快速產出、快速取得回饋,這是我們一而再地強調 "Iteration" 的最佳實踐!

星期二,我們是到淡水附近,國內某家非常大的保險公司,當然也是資訊單位,這兩個單位的 IT 資訊人員可是各約有 100 人左右,規模算是蠻大的。這家保險公司花了數百萬元購買國外某家非常非常知名大廠的 "Business Framework" 產品,光是設計文件就有數百 MB 之多,但,該產品卻並沒有被 "實現(Implementation)" 至某一平台,所以,該單位當然是想盡辦法希望能實用到該產品所提供非常浩瀚的領域知識(Knowledge),不過,兩年來,該 IT 單位委外其他 ISV 實現的成果一直不佳,無法符合他們的期望。

嗯,在前幾次與該單位的 meeting 會議中,我看過該產品,的確廣度與深度相當地浩瀚,從 Activity Diagram, Use Case, Object Model, E-R Model, Interface Design Model ...等,一應俱全。我與 Ringle 看過之後,也相當驚嘆,看來是 "名家" 的作品,直覺的判斷,應該是 Grady Booch 幾年前專案開發的產品。 讚嘆之餘,不過,我是認為,要實現好像沒有想像得那麼難,若連一個案例都沒有被實現出來,應該是分析的手法與角度有些問題。 一般呢,我們在輔導某家客戶之前,總是會由該單位提出案例,我們會利用一個 "prototype" 來實現整個架構與結構的設計呈現,眼見為憑,當然,總是需要證明一下我們的 "實力",也算是一種試煉,同時也比較能確保若談成輔導案時的一種信賴度。

在進行這一個 prototype 之前,其實前幾星期他們已先找過該產品在國內代理的 "某大廠",該大廠還委請國外的專家來進行 10 天的輔導與試行開發(與原型開發意義是一樣的)。但,結果仍非他們所期,雖然有完成功能面的需求實作,但,這不是他們想要的,他們要的是,在結構面要能完全支援該 business framework 規格的 Interface Design Model,而,往往一個小小的功能,可是要 "串" 上 10幾20個企業元件,這可還沒有牽涉到平台面的實作物件喔。

早上的會議作了一些其他議題的討論,包括教教她們如何抓出 "essential entity",然後該如何 mapping 至該 business framework 產品內 E-R Model 的那一部分。嗯,看看樣子,其實她們應該也是沒有期待這次的 prototype 能有所 "成果",這麼多人,花了那麼多時間,就是一直打不開這個 "潘朵拉密盒",只有我與 Ringle 當天在該 prototyping 會議,我們又不懂該領域的相關知識,怎麼會有可能給實現出來? 嗯嗯,我好像一直很樂觀,甚至,並不覺得這有難到無法解開,我是這麼認為,要能找到這把 "關鍵之鑰" 來打開寶盒,從 Domain 的觀點與 IT 平台實作的角度切入,都不太正常,那是個 "Business Framework",是軟體大師的作品,當然要從 "純" 軟體的角度切入才有可能解開!!

我與 Ringle 都是這麼確信,只不過,我比 Ringle 更來得樂觀就是了。中午吃飯時,Ringle 還有些 worry,但,我還是一直很有信心,信心的一個最主要來源就是,我是相信 Ringle 一定可以找到方法切入 prototype 的實作面的。要當場就能展現從浩瀚複雜表象的文檔裡,分析出脈絡,還要能具體實作,這可非 Ringle 莫屬,這我可沒辦法(我能看出問題與茫點,但實作的功力,可是差一大截),而且,我真的不得不佩服,Ringle 還真是這一行的天才,吃完飯後,他已經想好實現的方法了,而且,一想出來,馬上開始動手,除了利用 EA 定出好幾個層次的 Interface 元件後,找出要實現這一個功能的 operation(每一個 operation,又會呼叫其它的 interface,環環相扣),然後寫好使用案例,光只是一個查詢功能,就會呼叫到 10 餘顆的 interface,又要 "想像" 來寫出具體實現的物件 ...。三個小時,還真讓 Ringle 給完全寫出來了,只差沒有加上與資料庫連結的這一段細部設計。 她們部長(很漂亮與和藹的女生)以及其 SA 這次可真是相當驚嘆,我們所展現出如何分析的手法與思考邏輯,以及如何具體呈現的設計與產出,正是她們所沒有,也正是她們真正需要的,Bingo !!

總算睜大眼睛了,然後更希望我們能加上與資料庫連結的那一段細部設計與實作,所以再約定了下一個星期再展示算是第二個循環的 prototype。還是 Ringle,只利用星期日的時間一天 "順便" 學一下 Java Spring 與 Hibernate,整個 prototype,實現一個使用案例,從 JSP 到企業元件到資料庫,就給徹底的給實作出來了。 今天早上呢,就是去展示這個 refine 的 prototype,再作一次更細部的解說,大家都很滿意,也都很 Happy,明年度的顧問輔導合作,應該是很有機會創造 "皆大歡喜" 的成果出來的。 還有,這一次也可以說是這幾年來最有成就感的一次,要能 "看到" 一個內部都是完全以interface 為主的企業框架,可真是難得,然後透過我們手裡,而且別人還無法給實現,我們又只利用一到兩天的時間就 "打通" 脈絡,那一種喜悅與成就感,就是擔任軟體顧問職的最大價值了。

好了,回到家後,輕鬆一下,我的 "Relax" 就是玩「魔獸爭霸 3」對戰。工作是順利,但打電玩呢,就是不怎麼順利,又連敗了,這種即時戰略,要手與腦的全能協調,好像不太能適合我勒~

星期三早上,是應邀開車到台中「逢甲大學地理資訊研究所」作個演講。前幾個星期,已安排這天下午三個小時左右的演講,而在以 Email 溝通的過程中,他們的問題,是讓我有些失望,大概就是想找個 "方法論",找出 "How-to" 來解決他們在專案開發上遇到的種種問題,所以,問題諸如軟體專案從生命週期、分工合作模式、角色等面向,PIM, PSM 等模型該如何區隔 ...等。嗯,簡報該如何準備? 這些問題,好像整理成 50 頁也講不完,但反過來想,好像一頁的簡報也能回答眾多這類所謂 "方法論"、"流程論" 等等這些軟體工程的問題,就決定了,準備一頁簡報來 "應付" 在場聽者的各類問題,而且我還很有信心,所有的線索與答案,都只在這一頁的簡報就能找到的。

我又與 Ringle 一起開車到台中,中二高沙鹿交流道下車,開車過大雅,有個很寬的道路,就給它右轉進去,我知道逢甲的方向在那邊沒錯,但是,進去的是新闢的科學園區,然後走到底就沒路啦。Ringle 馬上 Show 出他的 Treo Palm 手機,有個 ShowMap 導航軟體,指出我們大概的座標,沒想到,沒有地圖顯示中科的道路。真諷斥也真有趣,剛好也讓我想到開場的主題:為什麼國內某家知名 PXGo 的導航軟體,沒有支援 Palm, Symbian 等手機 PDA 系統,為什麼只能開發 MS 相關的應用軟體?

耶? 大部分的人是這麼認為:西瓜靠大邊,所以當然選擇主流的市場啊! 問題是,我的意思是,那為什麼不乾脆所有的平台都一起開發? 為什麼一套產品一定非得要與某個主流平台綁那麼緊,各家平台的實作技術,有這麼困難嗎?

真正的問題在哪裡呢? 這個導航產品的 "結構面" 不見了啦,根本沒有用心作好 GIS(地理資訊系統) 這個領域的概念分析與分解,而成為真正有效的軟體結構模型。該產品推論應是以專案的方式進行,然後直接從需求面的功能觀點,直接給轉移至與平台緊依的實作面,至於結構面,肯定是給忽略掉了。若是如此,當然無法 "萃取" GIS 這個問題領域的核心,也就是把該領域的重要概念與術語,給表達至軟體的領域模型上,然後再參考要實現的各平台特性,包括 Win Mobile, Palm 等系統,加上平台面的細部設計即可。 咦? 好像也就同時說明了 PIM 與 PSM 模型的區隔了。

Architecture-Centric Process

我用一頁的簡報就是以這張圖來作整個流程開發說明的。細節上我想就不再做說明,或者,我應該會寫另一篇文章來專門解釋一張圖的 "涵意",它可以 "讀出" 相當多的故事與假設點的,同時也代表一件事,從需求面切入與從結構面切入或甚至直接從實作面切入,各有其開發模式,絕對不會是一樣的,而所注重的開發角色與其技能也都不盡相同,那更說明了一個重點,根本就沒有所謂的一種 "標準開發流程" 可以被複製與管理的!! (未完,待續 ...)

從開發角色看「兩地分工」開發流程
(縮略圖,點擊圖片鏈接看原圖)圖、從開發角色看「兩地分工」開發流程

關於專案經理與架構師

專案經理(Projct Manager)負責整個專案的資源統籌、時程控管、專案 Review 會議、開發各角色人員的協調等。關於專案經理,視專案的規模與性質等,可由 IT 部門的開發主管擔任,或亦可委外給專業的專案控管中心(Project Center)來負責。

而架構師(Architect),要能具規劃整體架構(architecture)的能力,包括建立架構觀點的模型(請參考 “最佳實務—以架構為中心” 內容),將架構的概念得以具體化呈現,也就是用大家都能同意的方式來呈現架構,讓架構變成具體的東西,可以系統化傳達、審查、加註和改善(一般而言,在系統規劃初期,架構師為了讓大家對未來開發的模式與技術等有概念,會以原型(prototype)的實作來作為架構的解釋與驗證(POC, proof of concepts))。同時,架構師也能懂得如何將抽象設計的那一面,與現實在專案上要使用的平台技術能相結合,所以架構師也需要瞭解高階設計、平台設計與程式實作等知識、技能與技術(只是,架構師一般以 “Mentor” 的角色來輔導,而不作量化重覆性的工作)。因為架構師需要瞭解 “純” 軟體相關太多的議題,包括抽象與實作,一般而言,會委外交給專職的軟體顧問公司來負責,例如,在國外,軟體界的大師 “Martin Fowler”,即創立這一類型的專業顧問公司(名為 ThoughtWorks)。尤其隨著專案的複雜度與系統整合的整體架構規劃考量等,專職的架構規劃與顧問公司,更形有其必要性。

「兩地分工」的開發者角色

上圖係以主要開發的角色,來看「兩地分工」的開發流程,與角色之間所需傳遞的主要設計產出。當 ISV(Independent Software Vendor) 在未來以 "軟體設計中心(Design Center)" 為事業開發的核心時,兩個最主要的角色:需求分析師(RA, Requirement analyst)、結構分析/設計師 (SA/SD, Structure Analyst/Designer)。

RA 負責蒐集與紀錄源自於客戶單位的需求,包括與領域專家(Doamin Expert)溝通後,所得出的整體性、精要性的企業流程(Business Process)與企業規則、邏輯等;而與未來資訊系統的操作使用者(Operator),所訪談的需求記錄,會比較是偏於局部性的表單流程與功能。

RA 負責整理並過濾有效的需求,據此而建構出能有效表達資訊系統功能觀點的使用案例模型(Use Case Model),包括使用案例圖與使用案例敘述 *註*。第二步則是 SA/SD 需要 “實現(Realize)” 使用案例圖中的每一個使用案例,即代表著可以被計量的系統功能單位(Functional point),SA/SD 主要的工作即在於,需要分析出系統的內部,在動態執行期間,會由那些物件的參與,來完成一個個使用案例的任務(實現系統功能)。找出物件、據個別物件的特性來作分類,然後賦予物件的責任,設計出實現使用案例的物件合作圖。這些工作由於已牽涉到系統的內部,一般稱之為 “系統的結構分析與設計”。SA/SD 的主要設計產出有:靜態結構的類別圖(Class Diagram),同時連帶產出表達資料庫的 “E-R 圖(Entity-Relationship)”、動態物件合作的循序圖(Sequence Diagram)。因為這些設計圖是偏向表達高階、較抽象的軟體設計觀點,而與實體平台的技術沒有絕對關連,一般稱之該階段的設計產出為 “PIM(Platform Independent Model)”。

*註*由於使用案例圖會需要界定系統範圍(System Boundary),以及找出誰會來使用該系統的主要參與者(Primary Actor),與該系統是否需要其它系統的服務(Supporting Actor)。一般而言,這已屬於架構設計的範疇,所以最好由架構師與 RA 共同參與規劃與需求分析,比較能得到表達使用案例圖的架構全貌(由 Architect 負責),以及個別使用案例的需求記錄(由 RA 負責)。

依「兩地分工」的開發模式,下一步驟即是將 SA/SD 的PIM 設計產出,交付給 “Coding Center” 的 “Technical Designer”,依其設計圖的規範,然後將系統面的考量加入到設計之中,並且擔任Programmer的監工。因此,“Technical Designer” 的工作並非屬於整體系統架構面的工作,相反地,“Technical Designer” 的工作主要是針對某一個特定的功能(也就是單一使用案例)進行細部的設計。加入跟平台面相關的元件資訊,讓 SA/SD 的設計可以實際交付給Programmer照圖施工,因此,“Technical Designer” 的設計和平台面的知識有絕對的關係。例如,若實作的系統平台為 Java/Spring 的平台,那麼,“Technical Designer” 在進行設計之前,就必須要先瞭解有關 J2EE 平台的相關技術。“Technical Designer” 的主要產出仍是有類別圖、循序圖與資料庫的 “Table Schema”,與 SA/SD 設計產出較不一樣的是,本階段的設計產出,已與平台的技術有直接關連,所以一般稱之為 “PSM(Platform Specific Model)”。

「兩地分工」成敗最主要的關鍵即在於 SA/SD 與 Technical Designer 之間,是否能達成有效順暢的溝通。一般而言,在專案開發啟始(Initial)階段,架構師會 “調和” 兩者之間的設計產出與其規範,使之不會違背整體架構設計,同時會確認 SA/SD 在實現使用案例的設計框架,未來得以順暢地移轉給 “Technical Designer (以下簡稱 TD)” 作平台的細部設計,並且會協助檢視 “TD” 的設計回饋,是否有違背 SA/SD 的結構設計。通常,Architect 會引導示範幾個兩者之間設計產出的案例,藉由早期謀和的過程中,把其中的風險因子降到最低,且能建立起兩者之間溝通的橋樑。

當 TD 的設計經驗證許可後,就準備移轉至實做階段開始程式設計。TD 除了將其產出交付給 Programmer,若 Programmer 尚未與 TD 取得足夠的溝通默契,TD 尚須考量實做面的諸多問題,例如 “Coding Style”,提供程式碼的範例及規範;”Code Generation”,如何使用 Modeling 的工具(如 RSA)來產生程式碼框架。若 Programmer 是比較資淺的,可能對 3-tier 平台之間物件的呼叫並不熟悉,TD 甚至必須親自撰寫原型程式(Prototype)並說明其程式的架構是如何表達的。

TD 的角色,可能比較接近傳統所謂的 CTO(Chief Technology Officer)角色。在單一的系統之內,系統內部的細節性的技術問題,TD 均必須有一定程度的素養。

說明

“整體—局部(Whole-parts)” 可以說是降低物件複雜度(Complexility)的最有效機制,因為,你可以將某一物件視為是一個 “整體(Whole)”,只要聚焦從該物件外面的角度來觀察該物件的特徵與行為,不用去關心組成該物件的細節,包括其組成的內部元素。其實,不探究物件內部的組成元素,就是一種封裝(Encapsulation)的效果,而封裝正是軟體設計人員在處理軟體的複雜度時,所必備的本質素養。

從外部的觀點來看待物件,焦點會集中在整個物件的特徵與行為,這是屬於 “用” 的角度,也就是只要知道如何能 “使用” 該物件所提供的服務(service)與取用該物件的資訊即可;那個時候需要 “剖開” 該物件的內部,來分析其內部的組成元素(也就是組成物件)? 這是屬於結構分析/設計的工作。

例如,以上圖「駕駛員」與「車子」這兩個類別來說,”駕駛員” 並不需要知道 “車子” 的內部結構,只要知道 “如何開” 這輛車子,也就是說,”駕駛員” 只要知道如何使用 “車子” ,包括開動、踩油門、轉動方向盤、排檔、煞車等,以及看得懂車子儀表板所提供的資訊就可以了。如果車子壞了怎麼辦?此時 “駕駛員” 可能必須 “打開” 車子的前車蓋,檢查內部是哪一些組件出了問題,更甚者,必須送至車廠檢修,利用檢修的電腦儀器來找出更細部的組件故障問題,這是屬於結構分析範疇的一種形式。而從汽車製造廠的角度來看車子時,更是需要對車子內部的結構詳加分析,並進一步的要能設計具體化為實體的產品,當然就必須對其組成車子各部分的結構元素要能瞭解彼此之間的關係,與各個結構元素的特性等。

從以上的敘述中可以得知,駕駛員不需要知道車子內部的結構,所以可以把車子視為是 “整體” 的單一個物件。但從汽車製造廠設計人員的角度來看車子時,卻是將車子視為是 “複合物件”,所以必須分析其內部的組成元素,這就是屬於結構分析與設計的範疇。

在生活面中,充滿了太多的 “複合物件” 的實例,例如,MP3 Player,筆記型電腦,主機板,樹木、乃至於中央山脈(包含了山峰、丘陵等局部) …等;企業面(Enterprise)中,公司(包含了多個部門)、訂購(包含了多個訂購細項)、看診(包含了多個診療記錄) …。

複合物件內部的結構元素是否也有可能是複合物件? 當然會的,例如,站在電腦主機板設計者的角度來看待 INTEL CPU 時,CPU 即為複合物件,但,卻是沒有必要分析其 CPU 內部的結構,只要知道如何與之溝通即可,而溝通的管道,就是透過 “介面(Interface)” 的呼叫;相對而言,INTEL 設計者就需要知道組成 CPU 內部的結構元素,例如包括邏輯運算處理器、暫存器、控制單元 …等。而事實上,CPU 其實看起來也像是 “主機板” 的角色,因為它也是需要 “組合” 多個內部的結構元素,但同樣地,它也沒有必要去瞭解如運算處理器內部的結構。

組成關係可以被視為是一種特殊的結合(a special kind of association)關係,所表達的是在於整體與其組成元素之間的一種結構(structure)關係。組合關係有兩種的表達方式,聚合(aggregation)在 UML 中是以空心菱形符號來表示;組合(composition)關係在 UML 中則是以黑色菱形符號來表示。兩者的 UML 表示法如下圖。

範例—聚合關係的UML表示法
圖1、聚合關係的UML表示法

範例—組合關係的UML表示法
圖2、範例—組合關係的UML表示法

聚合與組合關係的主要區別在於整體物件與局部物件的生命週期問題,聚合關係是屬於比較寬鬆的組成關係,以上圖 1為例,主機板包含了CPU、顯示卡、記憶體模組等組件,而這些組件均可以隨時視需要來 “抽換(Plug and Play, PnP)”,而圖 2 的例子則表達了比聚合更為嚴謹的結合形式,當 “Web Form” 實體(instance) 被系統給消滅(destroy)的同時,其所包含的組件,包括 “Button”、”Label”、”TextArea” 等,均會隨之與其整體(Web Form)被消滅,也就是說,整體與局部的組件,係為一整個生命共同體,當整體消滅時,其局部的組件也無法存續。

範例—駕駛員與汽車的組成關係

UML 表示法

範例—車子內部結構的組成關係
圖3、範例—車子內部結構的組成關係

“車子” 是一個 “整體(whole)”,其組成的結構元素包括了 “引擎” 與 “輪胎”,其多重性(multiplicity)個別是 1對1(一台車子有一個引擎)、1對4(一台車子有四個輪胎)。其中,車子與引擎的生命週期是等長,引擎無法被抽換,所以是以 “黑色菱形” 符號的組合關係來表示;車子與輪胎則是以 “空心菱形” 符號的聚合關係來表示。

注意的是,外界無法直接來操作與存取 “引擎” 所提供的操作,必須要透過 “車子”來取得引擎的相關資訊,所以 “駕駛” 是透過 “車子” 所提供的 “get引擎狀況” 操作,再由 “車子” 去詢問 “引擎” 的 ““get引擎狀況” 操作,來取得相關的資訊的。

局部(part)的零件,只能透過由整體(whole)來存取操作,而不可讓外界來直接存取使用,這是 “封裝(encapsulation)” 的基本設計原則。

※延伸參考:「觀察電動牙刷的結構設計

Java 程式範例碼

@原始碼下載

#001 public class 車子 {
#002 
#003 	public 引擎 m_引擎;
#004 	public 輪胎[] m_輪胎;
#005 
#006 	public 車子(){
#007 		m_引擎 = new 引擎();
#008 		m_輪胎 = new 輪胎[4];
#009 	} 
#010 	
#011 	public String 踩油門(){
#012 		String 滾動="";
#013 		
#014 		//需要實作四個輪胎的同時滾動,請賴小仁以 Thread 機制實作
#015 		for (int i=0 ; i<m_輪胎.length ; i++){
#016 m_輪胎[i] = new 輪胎();
#017 滾動 = 滾動 + "輪胎:" + i + " " + m_輪胎[i].滾動() + "; ";
#018 }
#019 return 滾動;
#020 }
#021
#022 public String get引擎狀況(){
#023 return m_引擎.get引擎狀況();
#024 }
#025 }
#001 public class 輪胎 {
#002 
#003 	public String 滾動(){
#004 		return "滾動中...";
#005 	}
#006 }
#001 public class 引擎 {
#002 
#003 	public String get引擎狀況(){
#004 		return "引擎狀況良好...";
#005 	}
#006 }

一樣,我們仍設計 “TestCar”,從駕駛的角度來看車子的運作情形。

#001 public class TestCar {
#002 	public static void main(String[] args) {
#003 		車子 m_car = new 車子();
#004 		
#005 		System.out.println(m_car.踩油門());
#006 		System.out.println(m_car.get引擎狀況());
#007 	}
#008 }

執行結果如下:

輪胎:0 滾動中...; 輪胎:1 滾動中...; 輪胎:2 滾動中...; 輪胎:3 滾動中...; 
引擎狀況良好...

關係

物件透過訊息(message)的傳遞至另一個物件,救代表了兩個物件之間,建立一種連線(link)的關係。觀察一段敘述:韓劇中的長今,因為當醫女要練習針灸的緣故,所以施針在一隻大頭吉娃娃身上。以 UML 物件圖(Object Diagram) 來表示如下圖:

範例—物件之間的連結關係
圖1、範例—物件之間的連結關係

上圖 1 的箭頭表示物件的可視性(visibility),說明長今(UML 物件圖內的物件名稱係加上底線表示之))物件傳送了 “針灸” 訊息給大頭吉娃娃物件。箭頭是否有可能為雙向? 當然有可能!例如,大頭吉娃娃因為被扎針後很痛而反過來咬了長今一口,如此兩個物件就構成了雙向關係,參考如下圖:

範例—物件之間的雙向連結
圖2、範例—物件之間的雙向連結


觀察物件之間的關係,再予以抽象化(abstract),也就是說再進而一步地來觀察這些物件所屬類別(class)之間的關係,依據其關係的性質與其結構性,可以發掘出類別之間的關係,分為三種:

  • 關連(Association)
  • 組成關係(整體—局部,Whole-parts)
  • 一般化—特殊化(Generalization—Specialization)

針對上述三種類別之間的關係,有必要個別來作詳細說明,並列舉範例、UML 語法與範例程式碼等。

結合關係(Association)

說明

「連結(link)」表示物件之間的關係;而「結合(association,或翻譯為關連)」就是將物件之間的「連線」關係抽象化,用來表示類別與類別之間的抽象關係。

結合代表著我們需要記憶類別之間的關係,通常是在某個情境當中,我們需要保存其相關資訊一段時間(視領域與需求而定,一般而言,可能保持幾秒到數十年的情況都有可能)。例如,以 「人(Persion)」與「狗(Dog)」這兩個類別來看,兩個類別是否有關係?若是我們塑模(Modeling)的對象為寵物店(Pet Shop)管理系統,那麼顯然這兩個類別就有值得保存兩者之間關係的必要性;但若開發的對象為人事管理系統,那麼,你又何必將「狗」這個類別與「人」類別關連起來,甚至,根本就沒有必要將「狗」類別給塑模包含至人事管理系統來的。

關於結合關係的 UML 表示法如下圖:

範例—結合關係的UML表示語法
圖3、範例—結合關係的UML表示語法

在結合關係內的每一個類別,都有其各自所擔任的角色(role),參考上圖 3,「Person」類別在與「Company」類別的結合關係中,是擔任了 “雇員” 的角色;反之,「Company」類別則是擔任著在這個結合關係中的 ”雇主” 角色,類別所擔任的角色,是表示在這個關連靠近類別的端點旁邊。

關連也可以為其命名,來凸顯兩個類別之間的一種行為。上圖 3的關連名稱被命名為 “雇用契約”,代表著「Person」類別與「Company」類別是一種 “雇用” 的結合關係。

範例

範例—駕駛員與汽車的結合關係

觀察周遭生活面面向,可以舉出太多一般性的類別與類別之間的結合關係。例如,”人開著汽車”,以這麼簡短的一句話,將其抽象化,就可以發現到,有兩個主要的類別:「人」與「汽車」,兩者必然存在著關係,否則,車子如何有可能會發動、行進呢?

UML 表示法

範例—Driver與Car 類別的關連UML表示法
圖4、範例—Driver與Car 類別的關連UML表示法

上圖 4表達了「Driver」類別與「Car」類別的 UML 表示法,其中,1 對 1…* 為多重性(multiplicity)的表示法,多重性定義了類別 A 會有多少個個體(instance)與類別 B 多少個個體會關連在一起。,上圖的多重性定義即表示為:一個駕駛員可以擁有1到多台車子。

Hint: 如何找出 "Car" 類別的屬性與操作?

從車子的角度來思考,車子應該知道什麼資訊(Knowing),以及應該負責那些行為(Doing)。請仔細的觀察,當你在開車時,你所看到的儀表板數據就是車子所應該知道的資訊,這些資訊就會成為「Car」類別的屬性;你所操作車子的動作,包括啟動、排檔、踩油門、轉動方向盤、煞車…等,就是車子所提供給駕駛的服務(service),也就是「Car」類別的操作(在程式語言的術語則稱為 “method”)。

Java 程式範例碼

原始碼下載
#001 public class Driver {
#002 
#003 	private String name;
#004 	private String 行照號碼;
#005 	public Car m_Car;
#006 
#007 	public String getName(){
#008 		return "駕駛員名稱:" + name;
#009 	}
#010 
#011 	public String get行照號碼(){
#012 		行照號碼 = "XXXX-XXXX-00128";
#013 		return 行照號碼;
#014 	}
#015 }
#001 public class Car {
#002 
#003 	private String 型號;
#004 	private String 車號;
#005 	private float 里程數;
#006 	private int 油表數;
#007 
#008 	public float get里程數(){
#009 		//需至里程記錄器取得資料
#010 		里程數 = 58000; //先給個測試數值
#011 		return 里程數;
#012 	}
#013 
#014 	public int get油表數(){
#015 		油表數 = 12; //剩餘 12 公升
#016 		return 油表數;
#017 	}
#018 	
#019 	public String get車況資訊(){
#020 		型號 = "蛋龜二號";
#021 		車號 = "GY-8228";
#022 		String 車況 = "型號:" + 型號
#023 		  + ";車號:" + 車號;
#024 		return 車況;
#025 	}
#026 
#027 	public String 踩油門(){
#028 		return "動作: 踩油門";
#029 	}
#030 
#031 	public String 排檔(String 排檔別){
#032 		return "動作: 排檔-> " + 排檔別;
#033 	}
#034 
#035 	public String 開啟(){
#036 		return "動作: 開啟";
#037 	}
#038 
#039 	public String 煞車(){
#040 		return "動作: 煞車";
#041 	}
#042 
#043 	public String 關閉(){
#044 		return "動作: 關閉";
#045 	}
#046 
#047 	public String 轉動方向盤(){
#048 		return "動作: 轉動方向盤";
#049 	}
#050 }

為了測試 “Car” 類別所提供的 method,我們可以先設計一個測試用的 Java stand-alone 的程式,以站在 “Driver” 的角度,來看車子的操作:

#001 public class TestCar {
#002 	public static void main(String[] args) {
#003 		Car m_car = new Car();
#004 		
#005 		System.out.println(m_car.開啟());
#006 		System.out.println(m_car.排檔("D"));
#007 		System.out.println(m_car.踩油門());
#008 		System.out.println(m_car.get油表數());
#009 		System.out.println(m_car.get里程數());
#010 		System.out.println(m_car.關閉());
#011 	}
#012 }

執行結果如下:

動作: 開啟
動作: 排檔-> D
動作: 踩油門
12
58000.0
動作: 關閉