1. 程式人生 > >煙花_易冷的專欄

煙花_易冷的專欄

自從1992年 Ivar Jacobson 發表了關於如何使用用例,從系統使用者的角度來提取軟體需求的方法的論文之後,這種方法已經逐漸流行起來。但是有一個最常見的問題是:當我得到了用例之後,如何才能把他們用程式碼實現出來?本文由兩部份組成,將會用一個實際的案例來說明這一點。如何從用例中提取需求,並加以分析,進一步將其轉化為可以直接進行編碼的格式。我希望能夠把這一過程講清楚,這樣你在當前的,或是下一個軟體專案中就可以使用它們了!

IBM Rational Unified Process(RUP) 提倡通過用例來提取系統中可操作的需求。 1軟體需求說明書,即 Software Requirements Specification (SRS),包括軟體的所有需求,其中包括很多相關的文件。用例就是其中的一個重要部分。 SRS主要包括以下幾部分:

  • 用例模型,包括:

    1. 用例圖:視覺化的描述系統使用者,和系統為使用者提供的服務。

    2. 角色定義:用文字描述系統功能,和系統所需的服務。

    3. 用例描述:用文字描述系統提供的主要功能。

  • 軟體補充規格文件:這個文件包括了整個系統相關的各種需求,和一些與對系統使用者和用例都不直接相關的隱藏需求。

在RUP方法中,這個需求文件是後續的分析和設計工作的起點。不同的開發方式,對專案的推動力是不同的,也就會產生不同的文件。如果軟體釋出之後,你還總是在不停的修補缺陷,你很可能根本沒有需求文件。只能從Bug報告中看出,軟體已經和最開始設計的樣子不一樣了。如果你在維護或者改進一個軟體版本(比如增加一項新功能),你可能有一兩個用例,他們描述了這些新功能和使用者之間如何互動,但是你不會有軟體補充規格文件,因為這些功能之外的部分並沒有改變。

在這裡,用一個虛構的軟體專案"green-field" 作為例子。這是一個面向物件的軟體專案,使用UML來描述系統中的各種概念與關係。讀者需要具有物件和類的基礎知識,熟悉UML 版本1.x 或者2.0中的類圖、順序圖和協作圖。

下面我們討論一下RUP中的用例分析。如圖1所示,將RUP中結構分析的結果整合起來。

顯而易見,嚴格來說的話,軟體開發過程應該側重於企業系統級的架構設計,以及軟體重用。但是基於以下三點原因,在這裡我不會長篇大論的講述結構分析:

  1. 我的目標不是結構分析設計,而是面向開發人員的更底層的日常工作。

  2. 這不是一本專著,沒有那麼多篇幅來講述結構分析。

  3. 根據我多年在軟體架構和軟體過程方面的諮詢經驗來看,只有很少的軟體開發組織能夠很規範的進行結構分析。如果你正在從事結構分析,你一定曾經經歷過本文中的一些內容。對一個新的,或是一個大型的軟體專案來說,採用結構分析是明智的選擇。但是如果你不太熟悉結構分析,本文的內容將會對你有所裨益。

用例分析的目的:

  • 找出用例中的執行流程、事件的各個類。

  • 通過實現用例,把用例的行為指定到具體的類。

  • 找出類的責任、屬性和他們相互的關係。

  • 規範地確定系統中各用例的職責。

我們也可以認為,用例分析的目標,就是把我們對用例的理解,轉變為與業務一致的形式,實現需求的價值。在用例設計的時候,我們把業務概念抽象成類、物件、關係、元件、介面等等,這些都與目標系統直接對應。

圖2引自RUP分析和設計概述部分,描述了需要使用用例分析的場景,和用例分析與其它步驟之間的關係。

在RUP [RUP2003]中的用例分析由幾部分組成:

  • 每個用例包括:
    1. 建立一個用例實現

    2. 用例的補充描述(如果需要)

    3. 從用例的行為中,找出分析類(Analysis Classe)

    4. 把行為指定到具體的分析類

  • 對每個分析結果的類:
    1. 類的職責

    2. 類的屬性和關係
      • 定義類的屬性

      • 分析類直接的關係

      • 分析類間事件與事件之間的相關性
  • 整合所有的用例實現

  • 建立跟蹤機制

  • 建立分析機制

  • 對用例分析的結果進行評估

請注意這些步驟的次序不是一成不變的。 你實際所採用的次序取決於你對於分析的領域的理解程度、你對RUP或UML的經驗、你所採用的模型、或者是你自己的確定分析類的屬性的一種習慣方式。(例如,以職責為中心,以行為為中心,或者以資料為中心) 關鍵只在於你是否能夠對問題建立一個準確的描述,而不是對我們的用例設計的準確描述--這將是本文的第二部分的內容)。我會遵循本文中的大多數(但不是全部)步驟,而且我會改變一些步驟的次序。在每個步驟的具體內容中,我會說明為什麼這樣做有助於RUP和OOAD的新手更好的學習OOAD。

如圖3所示,一些步驟把編寫用例和實現程式碼分隔開了。還列出了RUP中用例分析部分推薦使用的一些步驟。這張圖將會指出在後面部分中,我們的前進方向。

例子可以更好的說明問題。這個虛構的簡短例子是一個汽車租賃公司的網站系統。這種系統一般有幾個用例描述了為客戶提供的服務,如:

  • 預約汽車

  • 取消預約

  • 檢視租車歷史資訊

  • 檢視/編輯顧客檔案

  • 加入酬賓活動等

為了簡化模型,假設我們的服務只針對個人使用者,不針對企業使用者。

為了讓例子儘量簡單、易於理解,我們只分析其中的一個用例。描述如下:預約汽車.

用例:預約汽車。
  1. 這個用例從顧客提出他想要預約一輛汽車開始。

  2. 系統提示顧客輸入希望的租借和歸還汽車的時間、地點,顧客輸入以上資訊。

  3. 系統提示顧客選擇希望租用的車型,顧客進行選擇。

  4. 系統列出在指定時間、地點可用的,所有符合條件的汽車。如果顧客需要某輛汽車的更詳細的資訊,系統也可以提供。

  5. 如果顧客選定了某輛汽車,系統提示顧客輸入個人資訊:姓名、電話、電子郵件等等,顧客輸入以上資訊。

  6. 系統提供各種保護產品的資訊(如汽車損傷保險、乘客險等)並詢問顧客是否購買,顧客做選擇。

  7. 如果最後顧客表示接受這個預約,系統通知顧客預約已成功,給顧客一個確認。

  8. 當確認資訊出現時,這個用例就結束了。

必須用通俗易懂的方式來描述用例,不僅僅對web程式是這樣,即使是顧客走到營業點去當面預約也是一樣。 用例描述只需要指出結果,不需要詳細說明具體系統將怎樣操作、系統的客戶將怎樣操作。如果你用“營業員”代替上面的“系統”一詞,上面的描述就變成了一段對顧客到營業員處預約汽車的準備描述。其中的第7步給出確認的手續,在這裡就變成了書面格式的確認。

在設計web介面時,可以把多個步驟放在同一個頁面中,例如上面描述中的步驟2和3。在web環境中,第7步的確認,將會在預約交易報告頁面中呈現給顧客。

也請注意用例描述的文字風格。上面的描述是用正面的語氣,用現在時態來描述的。正面的語氣指的是描述清晰果斷,相反的負面的語氣指的是被動的語氣,和比較含混的描述。例如,“John投球”是正面的語氣,動作的執行者John放在動詞前面。這句話的被動語氣的描述是“球被John投出去”,或者更簡單的“球被投”,連動作主體也省略了。動作的執行者John放在動作後面。在所有的負面語氣的描述中,動作的執行者會出現在介詞短語中。讓你的用例描述清晰、一致。使用正面的語氣,現在時態。不要使用太多的詞彙,描述清晰就可以,不要使用太多無關的詞語,保持前後一致。例如,不要使用“顧客”,而是使用“客戶”,或者“商業夥伴”也不錯。如果同時使用這三者,你的讀者會認為你是在描述3種不同的使用者,他們的資訊和許可權也不同。

我們準備好的用例描述,可以作為下一步的起點。下面讓我們開始按照RUP中的用例分析過程來開始分析。

RUP中的用例分析過程的第一步是建立一個用例實現。在我們給出用例實現的定義之前,回過頭來看一下,到底什麼是用例?我們需要怎樣來確認用例?我們寫好的用例,是一個業務過程的描述,描述了顧客預約汽車的過程。它說明,我們會按照一些固定的步驟,按照固定的業務規則(例如在得到顧客的姓名之前不進行任何處理),也不為顧客提供那些無法按照指定的時間地點供客戶租用的汽車的資訊。

由於我們是在設計一個面向物件的軟體系統,我們的用例行為需要由我們系統中的一些物件和類來執行。但是迄今為止,我們還沒有任何物件和類,因此我們需要從用例中去發現這些物件和類。還需要指定每個類要執行用例圖中的哪些行為。

如圖4所示,用例實現實際上是一組UML圖,說明了我們都需要哪些類、它們的職責和它們的例項物件之間如何進行互動,來完成用例中的行為。

通常一個用例實現會由下面這些組成:

  • 包括我們所關注的用例中出現的所有類的一個UML類圖(有時也叫做合作類檢視), 和

  • 描述互動的物件,以及它們之間的呼叫關係的一個或多個UML互動圖 。UML定義了兩種型別的互動圖:順序圖 (如圖4),和協作圖。都很有效。

看起來我們有很多事情需要處理,是這樣嗎?是的,而且當你使用諸如Rational Rose或Rational XDE這樣的開發工具的時候,這項工作看起來更像一項家務管理工作,你只需要建立很多結構,存放你的用例實現。後面我們會完成實際的類和互動圖。但是現在我們先來明確一下我們的目標:一個類圖,一個或多個互動圖。

當你在進行分析的時候,你的用例描述只記錄了從系統外面的使用者角度來看,系統的行為是什麼樣子的。在概要的描述系統內部的一些不可見的操作的時候,這足夠了,但是按照這樣的描述,並不能完成具體的實現。

舉一個例子,看看上面描述的第四步:系統列出在指定時間、地點和可用的所有符合條件的汽車。如果顧客需要某輛汽車的更詳細的資訊,系統也可以提供。在這裡,我們有可供搜尋汽車資訊的資料庫了嗎?我們也許知道,汽車資訊由CICS(客戶資訊控制系統)管理,存放在MVS主機上,通過LU6.2 APPC可以訪問,但是我們不能確定這一點。讓我們來把這些我們要做的事情,特別是在我們系統之外事情的細節,來清楚的確定下來,而不是隻知道我們希望怎樣做。下面是補充了這些資訊的第四步,補充一個汽車資料庫: 系統通過訪問這個汽車資料庫來查詢汽車的位置資訊,並列出在指定時間地點和可用的所有汽車。

這裡我們給出了一個汽車資料庫的資訊,並且比較抽象的描述瞭如何用網頁來訪問它。這是一個很簡單的例子,但是讀者可以從中瞭解一些內容。

在RUP這樣的迭代開發流程中,從分析階段轉移到設計階段只用了很少的時間。對一箇中等規模的,四周左右的專案來說,你可能第一週用來獲取需求, 做你的分析和設計,然後後面3周都用來寫程式碼和進行測試。你用於分析的用例會側重於系統對外可見的行為,不過你可能需要細化這些用例的描述,增加更多的系統內部如何互動的描述。這樣你的顧客和分析人員才能確認你沒有遺漏一些重要的業務。請注意,只需要把用例描述細化到你可以很好的找出系統中的分析類的程度就可以了。 對於設計級別的類(諸如樹、堆疊、佇列、集合等等)可以在後面的階段完成(如設計階段)。

假設我們的系統是一個網站程式。當客戶需要的時候,我們會為他們提供私人線上預約汽車服務。我們要為這個實現的具體情況來細化我們的用例,不過不要過多的涉及到設計階段的工作。

這是預約汽車用例的更詳細的描述,還是側重於做什麼,而不是側重於怎麼做

用例:預約汽車(補充)
  1. 這個用例從顧客進入我們的預約汽車網站開始。

  2. 系統提示顧客輸入希望的租借和歸還汽車的時間、地點,顧客輸入以上資訊。 系統還提供給顧客一個選項,來選擇汽車的種類,如微型汽車、SUV、標準汽車,等等。顧客可以限定在一個或多個種類中搜索汽車。預設值是搜尋所有種類的汽車。如果顧客參加了我們的有獎租賃汽車活動,他可以在網頁上一個單獨的地方輸入他的有獎活動的編號。如果他填寫了這個編號,系統會訪問顧客的檔案, 來預先獲取相關的資訊。

  3. 如果顧客要繼續進行預約,系統在下一個網頁上,列出從資料庫中找到的所有符合條件(時間地點)的汽車,提供每輛汽車的基本費率,根據顧客的租用歷史情況還可以打一點折扣。如果顧客需要汽車的更詳細的資訊,系統從資料庫中查詢該資訊,並將其顯示給顧客。

  4. 如果顧客選定了一輛要預約的汽車,系統在一個新的網頁中讓顧客填寫個人資訊(姓名、電話、用於確認的電子郵件、信用卡發行商,等等)。如果系統中已經有該顧客的檔案,則調出系統中的相應資訊顯示在頁面上。有些資訊是必填項,另外一些則是選填項(如電子郵件)。使用者需要填寫所有的必填項。系統還提供各種保護產品的資訊(如汽車損傷保險、乘客險等)和單日的價格,並詢問顧客是否購買,顧客做選擇。

  5. 如果顧客表示“接受這個預約”,系統在網頁上顯示這次預約的概要資訊(汽車型號、日期、時間、選用的保護產品及其費用、費用總額等等),讓顧客確認。如果顧客填寫了電子郵件,系統會發送一封確認信到顧客的電子郵件地址。

  6. 當確認資訊出現在顧客面前時,這個用例就結束了。

在這個經過補充的用例描述中,我們清晰的描述了一個基於瀏覽器的程式的行為,描述了很多對顧客來說不可見的行為。但是並沒有提供設計級別的資訊。

不需要。但是請記住,補充細節,並不是指實現的具體細節。我們的目標是為了能夠有足夠的資訊來理解系統中的分析類,並與你的顧客或分析人員就所有用例的業務流程達成一致。 如果你覺得補充一些細節對於找出系統中的分析類有所幫助,那麼就加上它。

注意:把握這個細節的尺度會比較困難。需要時間和一些練習。多上手練習,向別人多詢問,還要記住當你無法決定的時候,應該稍微偏向抽象一點的描述。增加一些沒有寫上去的內容比較容易做到,但是要從很多雜亂的描述中找到有用的資訊可就難的多了。

這是因為抽象的用例是系統行為的最通用的描述(不過多的描述系統內部行為)。如果你還要開發一套客戶端/伺服器版本的預約汽車的用例會怎麼樣呢?如果你從詳細的用例(基於瀏覽器的系統用例),每次改變你的實現平臺的時候,你都得重寫整個用例。這個通用的描述與技術無關,特別是當你還沒有、或是無法確定系統的具體環境的時候,它的價值就得到了體現。另外,通用描述用例能夠讓你的業務分析人員能夠只看到系統如何工作的內容,而不是看到包含很多他們難以理解的技術細節的內容。

根據RUP過程,這一步的目的是找出分析類的候選範圍,這些類合作起來,可以完成用例的所有行為。因為我們還沒有任何類,所以我們的主要目標就是找出在汽車租借系統中的所有分析類。

這就引出了一個有趣而又重要的問題:什麼是分析類?有兩種答案。首先,一個業務級別的分析類是業務領域中的一個要素,與實現技術無關。例如,銀行系統中的銀行顧客、帳戶、帳號交易等等。而且它與系統的實現無關,不管是一個新的電子商務系統,或是一個從19世紀80年代就開始使用的借貸系統。

其次,RUP過程把這個定義擴展出三種不同的分析類:實體類控制類邊界類。RUP過程中的實體類大致上相當於前面提到的,業務級別的分析類。控制類與業務過程相關,它們控制整個業務的流程和執行次序。它通常控制一個用例中業務過程的執行。邊界類在系統與外界之間,為它們交換各種資訊與事件。邊界類處理軟體系統的輸入與輸出。

根據我在面向物件技術和麵向物件建模方面的教學經驗,我發現開發人員總是很快的從RUP過程中的實體類、控制類和邊界類這個環節,進入下一個設計環節, 沒有對問題進行足夠的分析。實際上,顯然控制類和邊界類都是面向技術實現的類,而不是面向業務的類。它們都是在設計階段所定義的系統設計模型中的一部分,而不是分析模型。因此,在這一步,我將會側重於業務方面的,與技術無關的分析類。在討論設計的時候再討論涉及技術的部分。請一直記住,搜尋與業務相關的類,是在RUP過程中的結構分析部分進行的,如果你的專案採用了RUP過程的話。

讓我們回顧一下,用例描述的是行為,也就是系統為使用者提供了什麼樣的服務。在用例描述中,沒有涉及面向物件的內容,但是這些描述是用來找出系統中的物件和類的。可以在很多地方,用各種各樣的方法來尋找類:

  • 領域的常識只是

  • 前一個類似的系統

  • 企業模型或供參照的體系結構

  • CRC (類/職責/合作關係)方法

  • 詞彙表

  • 資料探勘

尋找類的一個簡單的方法就是語法分析,下面我將加以說明。我們只要找出我們的需求中的名詞, 這些名詞(有些是形容詞+名詞):

  • 一些是類。

  • 一些會成為類的屬性。

  • 一些對我們的系統無關緊要。

找出預約汽車的用例描述中的這些名詞(跳過代名詞,如“他”),如下:

用例:為顧客預約汽車(補充)

  1. 這個用例從顧客進入我們的網頁開始。

  2. 這個系統顯示輸入框,來提示顧客輸入借出和歸還時的預約地點,和借出和歸還時的日期時間顧客輸入地點日期系統還提供選擇框,讓顧客來限定汽車型號分類 ,例如,如微型汽車、SUV、標準汽車,等等。顧客可以指定一個汽車分類進行搜尋,也可以指定多個分類預設的設定是在所有的 汽車 分類中搜索。如果顧客參加了我們的租賃有獎活動,他需要把他的有獎活動中的編號輸入到網頁上的一個獨立的輸入框中。如果這個輸入框 填寫了內容,系統會訪問顧客的個人檔案系統會提前查詢所需的 資訊

  3. 如果顧客表示希望繼續進行預約過程系統訪問汽車資料庫,查詢位置資訊然後在一個新的網頁中顯示所有的汽車資訊。這些汽車都是在指定的汽車分類 中的,並且在指定的地點日期時間中是空閒的。對每輛汽車系統都會顯示一個基礎費率,如果顧客租用汽車的歷史比較長,還可以打一些折扣。如果顧客想要看到某輛汽車更詳細的資訊系統汽車資料庫中讀取這些資訊,並把他們顯示給顧客

  4. 如果顧客選定了汽車來預約,系統顯示一個新的網頁,提示顧客輸入用於確認顧客身份的個人資訊,(姓名, 電話, 用來確認電子郵件, 信用卡發行商 )。 如果顧客檔案已經存在了,系統從中讀取所有已經填寫過的資訊。一部分輸入資訊是必填項,其它(如電子郵件)是選填項。顧客填寫所有必填的資訊系統還要顯示保護產品相關的資訊(如汽車損傷保險、乘客險等)和單日的價格,並詢問顧客是否購買這些保護產品顧客給出決定

  5. 如果顧客表示接受這個預約系統給出一個網頁,來顯示 預約的摘要資訊,(汽車的型別、 日期時間、所有選購了的保護產品及其費用租用總額),還為顧客顯示預約的確認頁面。如果 系統有使用者的電子郵件系統會發一封預約確認信到這個地址

  6. 這個 用例預約確認資訊出現在顧客面前的時候,就結束了。

注意每個名詞,或者形容詞+名詞的組合, 都被標出來了。有很多重複的,因此把每個詞單獨列入詞彙表1,按字母順序排序:

我們如何分辨出哪些候選的名詞才是真正的問題領域中的類呢?一個常用的方法就是,用一些簡單的問題來測試每個詞,如圖5:

  1. 這個候選是在系統的邊界之內嗎?
    如果不是,它可能是系統的使用者。

  2. 這個候選詞有某些明顯的與業務主題有關的行為嗎?
    (也就是說,這個候選詞可以擁有或者提供某些系統的服務或功能嗎?)

  3. 這個候選詞擁有明顯的資料結構嗎?
    (也就是說,這個候選詞擁有或者管理某些資料嗎?)

  4. 這個候選詞和其它候選詞之間有什麼關係嗎?
圖5:用來尋找分析類的問題

如果某一個答案是“不是”,那麼這個候選詞很可能不是一個類。再繼續檢查下一個詞。如果答案是“是的”,那麼繼續檢查下一個問題,如果所有的問題的答案都是肯定的,這個候選詞就是一個類。再繼續檢查下一個詞。

我們對每個候選詞做檢查,就會得到類似於表2的結果:

請注意租借地點已經被加進去了,雖然它不是用例的一部分。在與我們的業務專家(Subject Matter Experts,SMEs)們談話時我們發現,業務中會使用 地點來指代一個地址,或者一個租借部門。為了不致於混淆,我們一致統一使用單詞租借地點來表示業務地點,也就是發生租借和歸還的地方。

從這個列表中,我們列出了通過了問題檢查的詞,也就是下面列出的分析類

啊哈!在總共得30多個候選中,現在我們只需要面對選出的8個分析類。前面的四個問題幫助我們縮小了搜尋範圍,是個很有效的工具。

但是我們有沒有犯錯呢?有沒有漏過某個真正的類,或者把一個不該作為類的詞加進來了?這並不重要。RUP的迭代特性,會逐漸暴露出我們犯過的錯誤,並允許我們用盡量小的代價來修正它們。分析和設計的目標不是先把一切事情設計的很完美,而是當你需要確認這些事情的時候,才去確認這些。開始階段往往是最困難的,我們現在實現了物件(或者說類)從無到有的突破!重要的是我們已經起步了,而且我們能夠按照面向物件的方法一步一步的取得進展。

我們現在完成了RUP的用例分析活動中的前三步:

  • 對每個用例

    1. 建立一個用例實現

    2. 補充用例描述(如果需要的話)

    3. 從用例行為中,找出分析類

如果我們嚴格按照RUP過程進行,下一步應該是:

    1. 把行為分配給分析類

基於以下理由,這一次,我又會對RUP過程做一點小的改動:回顧一下我們的進展。我們剛剛找到了8個實體類,我們認為這些都是我們系統中的類。在我們做下一步之前,我們需要給這8個實體類增加一些內容,來確認他們類。

有三種基本的方法來充實我們的分析類:

  • 資料驅動的方法

  • 行為驅動的方法,和

  • 職責驅動的方法。

資料驅動的方法對於從事資料庫工作,或者從事過程語言程式設計的人員來說很常見。他們就是用資料和資料之間的關係來認識、描述世界的,因此會首先去尋找類中的資料-一般沒有什麼標準的方法去尋找類的函式或功能。這看起來不錯,但是資料只是工作的一半。實際上, 類的準確定義,包括了資料和對資料進行的操作。

行為驅動的方法有著雙重的成立理由。首先找出類執行的操作,從中決定這些操作涉及的資料中,哪些應該被這個類所擁有。這很棒,但是我們如何確認我們為類找出的操作之間能夠保持一致呢?如何區分操作和類,以明確這個操作是屬於這個類,但是 其它的操作要屬於同一個類,還是其它的類?我們需要某種方法來區分各個操作。這就是職責驅動的方法帶給我們的。

職責驅動的方法是自上而下的,從總體的類及其職責的檢視開始。首先定義一個類在業務中的“使命”,這個“使命”描述了這個類對外提供的服務。從軍事術語上來說,就是責任和策略。操作和資料是戰術層面上的(為戰略服務的,服從於某些目標、策略的)。 2

一旦我們確定了我們的類的職責,我們就可以設計一個分析類圖,來描述類間關係的整體結構。這個結構一般都是由業務領域決定的。一個UML分析類圖可以幫助我們視覺化的把這個關係的結構表示出來。

因此,我建議對標準的RUP過程做如下修改(次序的改變):粗體):

  • 對每個分析類

    1. 描述類的職責

    2. 在分析類圖上,建立分析類間的聯絡

    3. 把行為指定給分析類(找出操作)

    4. 描述每個類的屬性和關係

      • 定義類的屬性

      • 描述分析類間事件的相關性

在這裡,我們要對每個分析類進行處理。類的職責描述了這個類在系統中所提供的服務,而且其它類不會重複提供這些服務。各個類的職責不能重疊。

根據我們對汽車租借領域的理解,和對汽車租借專家、業務分析專家一起工作,我們在表3中,寫出了我們對每個分析類的職責的理解。

James Rumbaugh與其它人 3定義一個物件,或者說類,作為“一個概念,抽象,或者一個對業務來說有意義的,具有清晰定義的東西【我的重點】”。在類的職責定義中,首先要注意“清晰定義”,就是指明確指定了可以做什麼,不可以做什麼。

如果我們定義的職責出現錯誤怎麼辦?我們再重複一次,這無關緊要。我們已經開始,並且在不斷取得進展。當我們對系統瞭解的更多之後,我們對類的職責作出些調整是很正常的。這樣才能幫助我們把建模工作和軟體做的更好的。

我們已經確定了類的職責,下面將會設計一張UML類圖作為起點,來找出分析類之間的關係。 建立類圖有四個簡單的步驟:

  1. 確定要進行建模的類(我們已經完成了)。

  2. 確定哪些分析類具有與其它類之間的關係。

  3. 對於任意兩個之間具有關係的類,確定關係的語義 :是關聯、聚合、合成還是繼承?

  4. 對於非繼承關係,確認關係的多樣性(指另一個類的多少個物件可以被關聯到這個類的一個物件上面,類似於資料建模中的概念。)

通過以上步驟,加上我們前面確定的類的職責,我們得到了UML類圖,如圖6:

這個類圖上有三種UML關係,用不同的線區分出來。簡單的實線表明是關聯關係。用來表明兩個類之間的點對點的關係,每個類都會呼叫另一個類提供的操作。

在線上用實心菱形表示,在預約和保護產品之間的是合成關係(或者說不可共享的聚合關係)。這是個整體/部分的關係,或者說是一種擁有的關係。在這張類圖上,合成關係表示預約擁有管理0個或者多個保護產品,這些保護產品被預約所擁有。更進一步來說,合成關係表明如果預約這個物件被銷燬了,他所擁有的保護產品也必須被銷燬,因為當保護產品不再是預約的一部分之後, 就失去了存在的意義。

在線上用空心菱形表示的,在汽車租用和汽車之間的是聚合關係(或者說,可共享的合成關係)。這也是個整體/部分的關係,或者說是一種擁有的關係,但是當我們銷燬汽車租用的時候, 我們並不銷燬汽車。這符合常理:因為汽車不出租時, 汽車物件會暫時成為“孤兒”,但是並不被銷燬,只是把它提供給另一個汽車租用。

在關係兩頭的數字和* 符號叫做多樣性描述。這些符號表明有多少個例項可以被連線到一個例項上。例如可以和一個顧客有關聯關係的汽車的數量。或者,反過來,可以和一輛汽車有關聯關係的顧客的數量。在這個類圖裡面,我們的多樣性表明“對每位顧客,可以沒有汽車預定,也可以有多項汽車預定”。反過來,就是“對每輛汽車,可以沒有顧客預定,也可以有多個顧客預定”。

在分析中,我們試圖確認,我們能夠正確的表述和理解問題。對業務專家和分析專家來說,分析類圖是一個有用的工具,幫助他們和技術人員一起審查設計,並且將設計進一步推進。

現在我們有了類,職責和一張顯示類間關係的結構的類圖。但是迄今為止,我們還沒有涉及類的內部-沒有操作和屬性。而且類圖是靜態的。我們如何確認這些類能夠完成我們在用例中描述的業務過程?這就是下一步的目的,這是一個非常重要的步驟,因為它將會把用例描述對映到分析類的潛在的操作上面。

這些類如何協作完成預定一輛汽車這個用例?我們用UML互動圖來找出分析類之間的這些互動動作。回憶一下前面提到的順序圖和協作圖,就是兩種互動圖,它們是我們的用例實現的一部分,如圖7所示,這是一張分析級別的順序圖,描述了預定一輛汽車這個用例。

點選放大

你會看到,在這個圖中我已經引入了一個非業務類-UCController。這個用例控制類表示的是一個尚未進一步定義的類,它的職責是從使用者那裡接收事件和訊息。我發現大多數讀者都會感到困惑:一個業務類(例如出租地點或者預約)來接收使用者的訊息?因此我通常會給我的分析互動圖增加一個通用的用例控制類,來表示這個邏輯,而且方便了讀者的理解。在設計階段,我們會把這個類改名為ReserveAVehicleController,但是現在我用這個通用的名字UCController 來表示。

順序圖和互動圖包括幾乎相同的內容,它們只是表示方式不同而已。選用哪一種圖主要取決於是否方便和個人偏好。在順序圖中,物件按豎列對齊,按照從上到下的時間線來順序排列。標了數字和文字的水平線叫做訊息。在一個順序圖中,訊息的順序用它們的位置來表示:按照時間順序從上到下排列,因此排在下面的訊息就在排在上面的訊息之後發生。訊息從一個物件的時間線上開始,在一條時間線上終止(一般都是另一個物件的時間線),但是有時也會終止在同一個物件的時間線上,如圖7中的訊息21。

順序圖與協作圖相比,有一個非常明顯的優點,就是在圖左邊的指令碼。這些文字是從用例,或者場景中摘取的,對順序圖的描述。這些描述就是對預約汽車這個用例的一個簡短的描述。通過把這些指令碼放在圖邊,使得訊息的含義變得非常清晰。而且把訊息、物件和原先的用例相結合起來。用例中的每條語句都會對應圖中的一個或多個訊息,在順序圖上,這表示的非常清楚。

我想強調一下,在分析階段的互動圖中很重要的一點就是:訊息表明了意圖,而不是實現,也不是介面預約汽車順序圖上,訊息只是簡單的表明了,我希望接收訊息的物件做什麼事情,訊息並不代表一次函式呼叫。函式呼叫這些更具體的資訊,會在設計階段確定。但是現在,我們只需要在這個用例中,類的職責。

我們如何找出這些訊息呢?只要看看我們的職責的定義就可以了。例如,在第八步中,租借地點要決定在這個地點,哪些汽車是可用的。在第九步中,汽車租借要提供在這個地點,能夠滿足使用者要求的日期和時間的所有汽車。在第十步,租借中的每個汽車都需要回答它本身是否滿足某些租借條件。請注意所有這些知識都不在租借地點這個物件中。我們把這些知識分佈到了我們的所有分析類當中,因此每個類都可以按照定義來完成一部分工作。

在順序圖中,物件框沒有名字“:<classname>”,這些物件叫做匿名物件。但是也可以為物件起一個名字。如果我們有一個類叫做Account,我們可以這樣命名:

Account

如果我們建立兩個Account類的例項物件,FredsStashEthelsMadMoney,它們將是這樣:

FredsStash : Account EthelsMadMoney : Account

以左邊的一個為例,表明“FredsStash是Account型別的一個物件”。 如何決定是否給一個物件命名呢?如果系統中的一個實體類,有一個很清楚的名字,你可能想為它的物件起一個名字。如果你的圖中有一個示範的物件(類似於資料模型中的資料表例子),你也可以用一個有名字的物件。但是對大多數情況來說,匿名物件就足夠了。我們只關係類和物件提供了什麼服務,致於物件的名字並不會影響到物件的行為。

在分析中,我們會發現,類為了完成自己的職責,會需要一些屬性(也就是類的屬性變數)。從類的職責列表中,我們可以確定分析類的一些屬性。另外一些屬性要從常識中得出(例如,每個汽車物件應該有一個獨一無二的標識屬性,與實際的汽車上的汽車聯盟的標準汽車編號相對應)。

UML 說明:UML中的類在圖上分為三段,以Account類為例,如下圖所示:

圖8中的類圖上展示了汽車租借的分析類、它們之間的關係以及每個類所擁有的一些基本的屬性。這些屬性是由類的職責推理得到的一些很明顯的屬性。請注意這些屬性都沒有表明資料型別,因為資料型別是設計階段的問題。

在當前這一步中,我們只需要表明顧客類具有一個叫做地址的屬性就足夠了。至於地址這個屬性是什麼樣的,甚至需要不需要成為一個獨立的地址類,會在後面的階段中決定。你會發現汽車租借類還沒有屬性,它會變成系統的一個對外的介面。而汽車資料庫需要哪些屬性,則還根本沒有決定。今後的階段會解決這些問題。

分析機制指的是高階的系統構建元件,它可以提供解決特定領域問題所需要的一些服務,而不是技術方面。例如,在保險領域,保單中的資訊、宣告和其它內容,在整個保險管理期間都是需要的。這個需求用分析機制來說,就叫做:持久化:無論程式是否執行,都一直維護資料的資訊和狀態。請注意我們並沒有指定使用Oracle SQL,或是SQL Server這些特定的實現環境,我們只是列出了永續性,和我們後面會談到的設計機制實現機制。實現機制將會是與特定平臺或者軟體供應商相關的。

我們在表4中試著舉例說明,分析、設計和實現機制之間的關係:

一些通用的分析機制是:

  • 永續性

  • 通訊(程序之間,或是應用程式之間)

  • 意外處理

  • 事件通知機制

  • 訊息

  • 安全

  • 釋出(也就是說,被髮布的物件)

  • 遺留的系統介面

在汽車租借系統中,我們需要為這些指定分析機制:

結論

在“從用例到程式碼”的第一部分中,我們從一個用例開始,迄今為止已經找出了用來實現用例的類,它們之間的關係,和它們需要的屬性。我們還找出了一些分析機制,在今後的設計和實現中會用到它。

如果我們對另一個用例再一次重複這整個過程,我們會發現另一些分析類,定義它們的職責,它們之間的關係。也許還會發現一些新的分析機制,畫出新的協作圖或是順序圖,來說明這些類如何互動。這演示了RUP過程的遞增的特點:每個任務,每次迭代,都是在前面工作的基礎之上進行的。

我們已經完成了很多工作,但是我們還無法開始寫程式碼。下面我們的重點將會放到用例設計上面來,這就是本文第二部分的主題。