1. 程式人生 > >UML建模: 高階類設計中的常見錯誤及其糾正方法

UML建模: 高階類設計中的常見錯誤及其糾正方法

建模高階類設計中的常見錯誤及其糾正方法

常見錯誤

糾正錯誤

舉例

未完成設計中的屬性詳細資訊

確保使用其型別,可見性和初始值正式定義每個屬性。

見程式碼例11.1。 -DoctorID:INT = 0;

沒有完整的操作簽名

確保操作具有引數列表,可見性和返回值。

見圖11.16

直接從基本類設計中設計解決方案級別的類圖

在轉向系統設計之前,確保將序列和狀態機設計作為開發模型過程的一部分。

重溫關於過程的第4章,關於序列圖的第12章和關於SMD的第14章

過度使用多重繼承

儘可能避免多重繼承,至少在問題空間(實體)中

圖11.12僅作為理解多重繼承的示例提供; 不推薦。

在沒有良好繼承設計的情況下探索多型性

多型性需要仔細建立具有適當運算子過載的繼承層次結構。

研究圖11.10

混淆引用和值

通過引用是指多個源指向(引用)相同資料的位置; by value是在處理之前建立資料副本的位置。

設計中的介面和實現關係類或元件(元件在第17章中討論)可能很複雜,具有許多屬性和操作。當一個類很複雜,並且當它被另一個類使用時,複雜性只會增加。在設計過程中,複雜類將其公共操作的一小部分作為“子集”提供給其他類使用。這個操作子集可以被認為是“介面”。這樣的介面(或一組介面)代表了類的一組特定且有凝聚力的功能。然後,消費者類可以呼叫此功能來尋找提供介面的類的服務。

例如,考慮圖11.4,它通過介面顯示兩個類,PatientForm和Patient之間的關係。 Patient類是一個實質性的類,在設計層面,包含許多操作。然而,在PatientForm方面,並非所有這些患者手術都很重要。因此,當PatientForm必須使用Patient時,它不會直接使用Patient類,而是使用其中一個介面。

為實現這一目標,Patient提供了一個名為PatientRegistrationInterface的介面,可供PatientForm使用,而不是整個Patient類。如圖11.4中的示例所示,介面有助於將類的公共操作的子集提供給系統中的其他類。

介面也是類,雖然沒有操作實現的細節,也沒有所有屬性。因此,介面可以由類表示。

PatientRegistrationInterface提供屬於Patient類的操作子集,這些操作提供操作的模板,以及Patient類中介面的實際實現或實現。

顧名思義,介面只是類的“前端”,沒有底層的實現細節。因此,每個介面都需要通過相應的實現來實現。這種情況要求兩個類通過實現關係相關聯。設計中的實現關係表明哪個特定類實現了介面。設計中的聚合關係聚合關係是一種特殊的關聯形式。在聚合中,類在關聯關係中彼此更緊密地相關。聚合可以被描述為整體部分關係,其中實體由“其他實體”或“類別”組成。因此,聚合也稱為部分關係或包含關係。聚合的UML標準符號是一條線(類似於關聯),在關係中的“高階”(或容器或聚合器)類旁邊有一個菱形。

圖9.6(第9章)顯示了醫院和部門類之間的聚合或“有”關係。該圖顯示了“醫院有部門”,其中包含醫院一側的聚合鑽石。這意味著醫院由部門組成。

從技術上講,在解決方案空間中,此聚合意味著Hospital物件由Department物件組成。程式碼示例11.4顯示瞭如何在Java中實現聚合。

部門物件是醫院物件的一部分,序列圖中的部門物件的生命週期(在第12章中討論)取決於醫院物件的生命週期。實現關係:按引用和按值實現關聯和聚合關係的兩種方式:“按引用”和“按值”。圖11.5和11.6分別顯示了這兩種方法。

空心菱形(圖11.5)表明雖然“主”(或更高)類由“其他”類中的物件組成,但它在執行期間仍可獨立存在。這是因為它有自己的屬性,其值足以存在。

實心或實心菱形(圖11.6)表示構圖,這意味著主類完全由另一類中的物件組成,不能獨立存在。

因此,如果Hospital物件必須與相應的Department物件關聯,則Hospital物件可以“引用”Department物件。這是使用指向Department物件的“指標”。或者,Hospital物件可以使用Department物件的副本。如果建立了Department物件的副本,則聚合關係是“按值”。由於已建立Department物件的副本,原始物件將繼續存在。可以由系統中的其他物件建立和使用Department的其他副本。由於嘗試使用部門的各種物件嘗試並更新自己的部門副本,因此部門的多個“按價值”副本可能會產生混淆。在執行期間,可以從多個源更新Department物件,從而導致同步問題。

當使用“by reference”來實現兩個類之間的關係時,Hospital物件不會複製Department,而是指向或引用Department。因此,Department物件在系統中是唯一的。需要Department物件的其他物件引用它而不是複製它。

兩種實施方法之間的比較可歸納如下:

◾在“按價值計算”中,醫院和部門物件“嚴密”卡住。這意味著在建立Hospital物件時,會在其中建立Departments;同樣,當Hospital物件被銷燬(刪除)時,它必須刪除Department物件。此外,當Hospital物件從記憶體位置A移動到B時,Department物件也應從相關位置A移動到B.

◾“參考”,醫院和部門僅通過參考相關。

這意味著當Hospital從記憶體位置A移動到B時,不一定要移動Department物件,並且Hospital物件可以繼續引用它。

引數可見性物件可以通過操作簽名作為引數傳遞給其他物件。這種物件對系統中其他物件可見的程度與關係的設計有關,物件在設計中可以具有以下可見性:

◾全域性可見性使物件在執行時對系統中的所有物件可見;這可能會轉化為一種聯想。

◾類可見性 - 使物件僅對類或其他物件和類中的操作可見;這也使得兩個類代表通過關聯相關的兩個物件。

◾功能可見性 - 保持物件可見,僅對給定的功能或操作可用;這轉化為階級到階級關係中的聚合。

◾欄位(或引數)可見性 - 使物件僅作為引數列表中的欄位可見。

根據實現語言,程式設計師可以選擇使引數物件對系統的部分(或全部)可見的選項。

多重性和物件圖設計中的多重性多重性指示將參與關聯或聚合關係的類的例項(即,物件)的數量。多重性在關聯關係的任一側顯示為數字,表示在執行時將涉及此關係的物件範圍。

最初在分析期間指定多重性。這些多重性源自用例中指定的業務規則。在設計過程中,進一步更新和改進了分析過程中產生的多重性估計。對於良好的設計,建議在所有關聯和聚合關係上指定多重性。這是因為,除了指定業務規則之外,稍後還會在資料庫設計期間使用多重性來開發<< table >>類及其關係。由於多重性是對資料庫設計的重要影響,因此在第13章中再次對它們進行了單獨討論。

第9章(圖9.7)討論了類圖中的多重性規範。多重性可以解釋如下:

◾一個醫院物件必須有一個部門,但它可以有許多部門。

CODE EXAMPLE 11.4: FOR AGGREGATION RELATIONSHIP IN JAVA

class Department

{

int DeptID;

String DeptName;

int[] AllDoctors;

int HospitalID;

public String getDeptName()

{

//code to get DeptName

}

public int getHospitalID()

{}

public String getHospitalName()

{}

public int getDoctors(Doctor DoctorArray[])

{}

}class Hospital

{

int HospitalID;

String HospitalName;

Department[] AllDepartments;

Address HospitalAddress;

public String getHospitalName()

{}

public int getDepts(Department DepartmentArray[])

{

//get list of departments in the hospital

}

}

物件圖解釋多重性請注意,在指定多重性時有許多“可選項”。例如,當指定多重性0.1時,它表示在執行時,在該關聯的那一端可能沒有或一個物件關聯。因此,通過檢視多重性,人們不能總是確定將與另一個類的物件連結的物件的確切數量。物件類Model-3

可以繪製181圖表,以便在視覺上顯示在執行時可以連結到另一個物件的物件數量。

圖11.7顯示了對應於同一圖中的簡單類圖的物件圖。該類圖上的Doctor-Department關聯中的多重性通過物理物件及其連結轉換為物件圖。圖11.7顯示了屬於DOCTOR類的aDoctor物件和屬於其對應的DEPARMENT類(或DEPARTMENT類的派生類)的其他四個部門物件。此圖描繪了Doctor-Department關係的執行時方案。

請注意,每當乘法指定可選性(或範圍,如本例1.4),則一個物件圖不足以顯示其整個範圍。例如,在系統的某些其他執行中,只有兩個或三個Department物件將連結到aDoctor。無需為每個可能的多重性繪製物件圖。相反,只有在關係複雜且重要且需要視覺化表示時才繪製物件圖。此外,在執行時,需要進行檢查以確定物件的數量和存在的連結。這些檢查可以通過這些類中的特殊操作來實現,以驗證對應於多重性的連結的存在。最後,當顯示物件之間的連結時,它們的可見性(如前所述)也會得到改進。

集合類和多重性如前所述,多於一的多重性表示為該類例項化的眾多物件的範圍。例如,如果類Patient具有多於一的多重性,那麼從該類例項化有許多Patient物件。分析描述Patient類行為的用例揭示了功能 - 技術操作 - 也有兩類:

◾一組操作處理單個Patient物件。這些是與單個患者的行為相關的所有操作,例如createPatient(),calculateAge(),getDiagnosis()或changeDetails()。

◾另一組操作涉及一組或一組Patient物件。這些操作包括能夠對一組患者進行儲存,檢索,分類和操作的操作。

此類操作的示例是listPatientByName()和totalPatients()。

這些操作不適用於單個Patient物件,因此不應將它們放在Patient類中。

適用於物件集合的操作指向需要“容器”或Collection類。這樣的Collection類在圖11.8中顯示為PatientCollection類。容器或Collection類是其例項僅指向屬於另一個類的列表或物件陣列的類。集合類以開發環境中可用的容器類(實現語言)為模型。此類容器類的示例包括集合,陣列,列表,字典,堆疊和佇列。

容器類通常在UML中被建模為引數化類。如圖11.9所示,其中list類具有表示集合的Item。在使用UML對收集方案建模時,此專案由Patient替換。

設計繼承中的繼承和多型(在第9章的圖9.3中討論)關係意味著一個類共享另一個類的結構和行為。子類從超類繼承三個特定元素:屬性,操作和關係。實際的繼承層次結構通常是三層深。

程式碼示例11.5反映了“外科擴充套件部門。”此程式碼示例中顯示的繼承關係也允許類重用。

在設計中融入多型性多型性是軟體工程的基礎之一(第1章)。多型行為意味著,在執行時,相同的訊息具有不同的行為效果。呼叫物件將相同的訊息傳送到被呼叫物件。但是,接收訊息的被呼叫物件可以屬於許多繼承類之一。因此,接收的訊息以不同的方式執行 - 取決於已經例項化的接收物件。

在設計中加入錯誤和例外結合錯誤檢測和錯誤處理是良好設計中的一項重要活動。錯誤處理是在執行時檢測物件中的錯誤的機制。錯誤處理還指定發生錯誤時物件要採取的操作。物件中的錯誤示例包括錯誤的引數列表,引數中的錯誤值或不適當的返回值。

雖然錯誤通常是由於呼叫物件的錯誤呼叫而發生的,但在良好的設計中,接收物件以優雅的方式處理錯誤仍然很重要。

在沒有這種錯誤處理設施的情況下,系統“崩潰”並且使用者沒有控制或補救。

設計中的良好錯誤處理包括由發生錯誤的物件以外的物件處理錯誤。這提高了設計質量,因為通常在整個系統中並不總是知道執行時物件中的錯誤的影響。大多數實現語言都提供內建的錯誤處理功能,支援強大的設計。

單獨的錯誤監視物件可以繼續傳送驗證和驗證物件完整性的測試操作。這些“測試物件”繼續通過傳送測試訊息並在訊息被執行後檢查其狀態來監視主物件的狀態。

簡單程式碼示例11.9顯示了在HMS的Patient類中包含錯誤處理(請注意,這是一個不是特定於程式語言的虛擬碼示例)。此示例顯示了患者物件獲取計劃時發生的情況。此計劃基於索引(從1到10,因為在此示例中僅允許10個計劃)。當索引超出範圍時,會發生錯誤。此錯誤處理機制不會“崩潰”系統,而是顯示錯誤“索引超出範圍。

例外僅用於未預期的“特殊”情況。因此,異常不是普通的錯誤情況,可以通過類中的“if-then-else”邏輯來處理。類別設計中包含例外情況,以處理錯誤未被類別計劃和不被類別理解的情況。程式本身的演算法或邏輯中的錯誤不是例外。但是,這些錯誤的演算法在執行時“丟擲”異常,並且錯誤處理是“捕獲”或處理意外錯誤的機制。

屬性標識,命名和定義屬性是類的屬性的名稱。每個類都有表達其某些屬性的屬性。屬性在很多方面與類相似,但不相同。例如,識別屬性的過程與類名詞分析的過程相同。然而,與類不同,屬性沒有自己的行為。事實上,這種缺乏行為是確定屬性為屬性而非類的原因之一。圖11.13顯示了在解決方案空間中設計期間如何定義和顯示屬性的詳細資訊。

Person類的屬性示例是LastName,FirstName和DateOfBirth在UML中,每個屬性都有一個名稱和一個型別。型別可以是模型中的任何資料型別,類或介面。

命名屬性與類一樣,屬性也稱為單數常用名詞。建議使用樣式指南來指定屬性的命名約定。例如,命名屬性的通用標準是用大寫字母開始它們。此外,如果必須為屬性名稱連線兩個或多個單詞,則應將它們與以大寫字母開頭的每個子單詞放在一起。不使用下劃線發現屬性通過分析用例文件中的事件流,可以發現許多屬性。

作為第一個剪輯,不適合成為一個班級的名詞可以成為一個屬性。

定義類時會發現其他屬性。領域專家也提供了良好的屬性。在分析和設計階段,類中屬性的發現和細化是一個迭代和增量過程。另外,在設計期間,為屬性提供更多細節以使得能夠實現相應的類。

屬性(資料)型別通過宣告資料型別在設計中進一步定義屬性。資料型別列在屬性名稱後跟冒號(:)後面,如圖11.14所示。有三種類型的資料型別:

◾開發語言提供的資料型別,例如INT(對於整數)和CHAR(對於字元)。

◾使用者定義的資料型別,例如Color,可以描述汽車的顏色。

◾使用者定義的類,例如Patient物件將具有一個名為Address的屬性,它本身就是一個類(圖11.15)。

“使用者定義”型別提供“偽”語言,允許設計者擴充套件語言以用於編碼系統。一些開發語言比其他語言提供更多資料型別;例如,某些語言中可能未預定義Date資料型別,這需要在編碼期間建立它。開發語言的選擇不應該影響類的設計,因為如果資料型別尚未在特定語言中預定義,則資料型別在編碼期間始終可以由使用者定義。

屬性值雖然屬性是類中資料項的“定義”,但屬性值是資料項的“實際”值。例如:

◾屬性是lastName

◾屬性值是“波特”

◾另一個屬性值是“Sharma”等等......屬性值也有助於提供狀態和物件;例如,在Account類中,如果屬性dateClosed的值為0,則關閉相應Account物件的狀態。

此外,如果dateClosed屬性包含實際日期,則關閉Account物件的狀態(有關狀態的更多詳細資訊,請參閱第14章中有關狀態機圖的討論)。

設計屬性中的常見錯誤屬性與類類似。這種相似性是設計屬性時出現一些常見錯誤的原因。

屬性設計中的常見錯誤包括:

◾將類命名為屬性,例如,將Address設定為Patient的屬性,而Address更適合作為一個類本身

◾將操作命名為屬性,例如,getName是屬性的錯誤名稱,因為它意味著操作而不是類的特徵

◾將屬性值命名為屬性,例如,Sam是名為Name的屬性的值,但不是屬性本身

◾在需要時不初始化屬性,例如,DateOfBirth是應該初始化為“00”的患者類的屬性

◾不提供適當的可見性 - 屬性預設情況下應具有私有可見性,儘管偶爾全域性屬性可能具有公共可見性。不提供可見性或提供不適當的可見性是設計錯誤。

◾為屬性提供錯誤的屬性型別,例如AccountBalance:CHAR;帳戶餘額的屬性型別應為CURRENCY或DOUBLE。

when在需要時不提供屬性構造型。 Stereotype是UML中的分組機制,如果一個類中有大量屬性,那麼按照它們的原型對它們進行分組總是一個好主意。

屬性的常見構造型示例有<< entity >>,<< business >>,<< date >>,<< counter >>等。

操作標識,命名和簽名圖11.16顯示瞭解決方案空間中的操作的詳細資訊。

瞭解類中的操作類中的操作使類能夠執行其職責。它們是可以由從該類例項化的物件執行的操作。

操作示例包括:

◾檢查賬戶,客戶的有效性

◾從資料庫建立,讀取,更新和刪除(CRUD)操作類中的操作還提供實現封裝的機制。操作提供可由其他物件呼叫的內部和外部服務。換句話說,類具有向需要該資訊的其他物件提供資訊所需的所有操作。例如,如果一個物件向Person物件詢問該人的年齡,那麼Person物件將從dateOfBirth屬性和todaysDate計算年齡本身以提供答案。通過應用封裝的基礎,Person物件不允許系統中的任何其他物件訪問其屬性(在此示例中為dateOfBirth)。

命名操作操作是系統中其他物件的類的介面。出於這個原因,應該命名操作以指示其結果,而不是操作背後的步驟。這是因為操作中的步驟可以隨著類本身的進一步開發和改進而改變。這需要重新命名操作並修改它與之介面的任何其他類 - 如果操作是以它的功能而不是它提供的命名。

以下示例說明了命名操作的原理:

◾get​​Balance() - 這是一個命名良好的操作,因為它表示來自“呼叫類”視點的結果。

◾calculateBalance() - 命名不佳,因為它表明必須計算餘額,而可以有很多方法來達到平衡。此名稱代表實施決策。但請注意,如果在類中的另一個操作使用calculateBalance(),那麼操作的這個名稱是完全可以接受的。

瞭解操作簽名操作提供系統的行為。最初在用例中識別並記錄這些行為。然而,在設計期間,操作“簽名”包括更多細節,從而可以實現操作。因此,在設計期間,操作定義將擴充套件為包括引數列表(也稱為引數),它們的資料型別以及操作的返回值資料型別。

例如,Patient類中的操作可以完全定義為getAge(int PatientID):int,這意味著操作被髮送患者ID(這是一個整數),並返回一個整數,即患者的年齡。 (請注意,Patient類本身將包含必要的屬性DateOfBirth,用於計算年齡 - 這是封裝。)在分析過程中,包含簽名是可選的;但是,在設計過程中,簽名是強制性的。

操作的簽名也可以表示關係。如果操作引數中的類(或從操作返回)是基類(例如,字串),則該關係不會在圖中顯示。對於其他類,該關係通常顯示在一個或多個類圖上。可以將操作組合或拆分為類,並將其他操作新增到類中以實現它。

圖11.10通過一個簡單的例子顯示了多型性。 在這個例子中,Car類繼承了兩種型別的汽車:FourWheel和FamilyCar。 Car是包含方法(操作)moveCar()的超類。 此操作是由FourWheel和FamilyCar類中的moveCar操作“過載”的虛擬操作。 在程式碼示例11.6中可以看到這種設計的效果。