第八章分析與設計階段– 物件導向設計(OOD)

Download Report

Transcript 第八章分析與設計階段– 物件導向設計(OOD)

第八章 分析與設計階段 –
物件導向設計(OOD)
軟體工程
-物件導向程式設計與UML系統分析實作
綱要



前言
類別的規劃
物件導向設計階段 (OOD)



建立動態模型
建立設計模型
總結
前言

OOD目標:


將OOA階段的分析模型,繼續透過反覆的修
正與演進,使得整個模型能更趨於完備。(即:
足以拿來實作之完整類別模型)
手段:

以圖7-4為藍圖,繼續以各種基於好的物件導
向設計原則進行檢視。
需求階段
建立Use-Case模型
建立分析模型
建立動態模型
(互動與狀態圖)
建立設計模型
(詳細類別模型)
分析
階段
(OOA)
設計
階段
(OOD)
進入實作階段
物件導向之分析設計程序 (圖7-4)
主要任務




建構互動圖(Interaction Diagram)
建構狀態圖(Statechart Diagram)
建構詳細的類別模型(Detailed Class Diagram)
撰寫虛擬程式碼(Pseudo-Code)
在這之前…類別的規劃

問題:


在OOA階段,「使用者與系統的互動媒介」
似乎並不明顯?
人機介面(Human Interface):


ATM(自動提款機)的輸入裝置
圖形使用者介面(Graphical User Interfaces)
範例探討(1)

一般商務系統至少具備了…



核心類別:”訂單”
功能:顯示訂單明細
Frame
假設以GUI為人機介面
訂單
+顯示訂單明細()
Button
Edit
Label
(Cont.)

就物件設計的角度來看,顯示訂單明細的
功能由”訂單”物件自己來負責,似乎是
理所當然!不過…

考慮以下的狀況:

若系統中存在上百個有需要跟使用者直接做輸
入輸出的類別,一旦使用者與系統互動的介面
需求有所變更時,套用此設計方法將有何影響?
Note: 介面需求變更常發生在文字模式(Console-based Mode)
與視窗模式(Window-based Mode)之間的轉換,或是更換不
同的視窗工具組/函示庫(Windows Toolkit/ Library)。
(Cont.)

主要影響:


此時會有很多的類別需要跟著修改(如:上例
中的”訂單”) 。
問題根源:

核心類別與使用者介面之間的過度耦合
(核心類別被使用者介面綁住了!)
典型解法

隔離核心類別與使用者介面

邊界類別(Boundary Class)


實體類別(Entity Class)


專司使用者介面
即核心類別 (通常為永續物件(Persistent Object))
注意圖中的相依關係

理論上,核心類別對邊界類別一無所悉。
使用者介面(邊界類別)
顯示
核心類別
(實體類別)
(Cont.)

變更訂單介面等邊界類別(如:Form/Frame、Edit、
Button),並不用跟著修改”訂單”類別。
Frame
訂單介面(邊界類別)
訂單( 實體類別)
+ 顯示訂單明細()
Button
Edit
+ 取得訂單明細()
Label
範例探討(2)

基於上例,考慮另一個需求:


假設透過邊界類別需要完成的是 - ”替某顧客
建立訂單的程序”
可能之類別結構以及物件合作關係如下…
顧客
1
新增訂單(邊界類別)
+ 輸入顧客資料()
+ 輸入訂購明細()
+ 登錄訂單()
0..*
訂單
1
1..*
訂購明細
4: 建立物件<<create>>
: 顧客
1: 輸入顧客資料( )
2: 輸入訂購明細( )
3: 登錄訂單( )
: 新增訂單(
邊界類別)
8: 加入
5: 建立物件<<create>>
: 訂單
: 操作者
7: *[針對每筆訂購明細]加入
6: *[針對每筆訂購明細] 建立物件<<create>>
: 訂購明
細
(Cont.)

部分流程控制的工作將落到邊界類別


邊界類別需協調多個實體類別(如”顧客”、”
訂單”以及”訂購明細”)來完成登錄程序。
考慮同樣的狀況:當使用者介面需求有所
變更時…

儘管實體類別不需修改,但邊界類別中的部分
商務邏輯(控制流程)卻還是得重新撰寫!
再次隔離…

在實體類別與邊界類別間加上控制類別
(Control Class)

由控制類別包辦商務邏輯相關的控制流程
實體類別
使用者介面(邊界類別)
顯示
控制類別
實體類別
實體類別
(Cont.)

此時邊界物件便不需涉及特定之控制流程,
就算邊界物件有所變更時,嵌在控制物件
中的控制流程仍可被保留下來。

此外,許多錯誤處理的相關動作,也可由
控制物件集中處理,不需分散在邊界物件
中。
顧客
1
新增訂單(邊界類
別)
+輸入顧客資料()
+輸入訂購明細()
+登錄訂單()
登錄訂單(控
制類別)
+登錄訂單()
0..*
訂單
1
1..*
訂購明細
: 顧客
5: 建立物件<<create>>
1: 輸入顧客資料( )
2: 輸入訂購明細( )
3: 登錄訂單 ( )
9: 加入
4: 登錄訂單( )
:新增訂單(
邊界類別)
: 登錄訂單( 6: 建立物件<<create>>
控制類別)
: 訂單
: 操作者
7: *[針對每筆訂購明細] 建立物件<<create>>
8: *[針對每筆訂購明細]加入
: 訂購明
細
小結

對類別作如此細膩的切割動作固然需要不少時間
成本,但將有利於系統日後的擴充與維護。

依照類別(物件)的性質可區分成:

邊界類別(Boundary Class):


實體類別(Entity Class):


負責使用者介面
負責問題領域的核心資料
控制類別(Control Class):

負責邊界物件與實體物件的協調(流程控制)
(Cont.)

其他應用:


資料庫存取 (更換不同的資料庫系統也是很常
見的事情)
Note:此概念源自於MVC (Model-View-Controller)
Pattern。
物件導向設計階段 (OOD)

主要執行步驟,細分如下:




加入邊界類別與控制類別
建構互動圖(Interaction Diagram)
建構狀態圖(Statechart Diagram)
建構詳細的類別模型(Detailed Class Diagram)





決定類別行為(Operation)
加入設計階段的類別
決定可見度(Visibility)
決定資料型態
撰寫虛擬程式碼(Pseudo-Code)
Case-Study:
加入邊界類別與控制類別

考量是否需要控制類別

被協調的兩端在性質上是否有明顯分野


被協調的兩端是否有需求變更之虞


例如:邊界類別與實體類別之間
例如:軟體元件或(類別)函示庫
通常…

使用者(Actor)與Use Case之間,會是置入控制
類別的適當位置
以訂房程序為例

訂房邊界類別


泛指所有相關的視窗元件 (為簡化起見)
資料庫控制類別


專司資料庫存取動作
降低實體類別與資料庫介面之間的耦合性
訂房邊界類別
訂房控制類別
核心類別
(實體類別)
資料庫控制類別
資料庫相關的
介面
(Cont.)

Note: “訂房控制類別”與”資料庫控制類別”均
有機會指涉到許多實體類別,這跟實際運作的流
程有關,在此暫時省略這些關係。
Case-Study:建構互動圖

參考來源

類別圖(模型)


取得物件(類別)互動時的主角
Use-Case腳本

取得物件的互動流程
(Cont.)

是否物件間所有的互動都要畫出?


畫出的互動情形越多,將有利於決定詳細
類別模型時的精確度。
基本要求:



至少要描繪出每個Use Case腳本的主要流程
原則上夠用就好!
其他考量因素:

時間、成本、軟體系統種類以及是否有透過輔
助工具(CASE Tools)…等
(Cont.)

互動圖要詳細到什麼程度?


基本原則 - ”盡量保持簡單”
把複雜的互動情況拆解成多張互動圖
: 訂房邊界類別
: 櫃臺人員
初始化
: 訂房控制類別
: 資料庫控制類別
提供可以選擇之房型
、日期範圍等畫面
選擇(輸入)房型、起始時間和結束時間
查詢空房
查詢空房( 房型、起始時間和結束時間)
在此櫃臺人員會同
時見到房間號碼
[存在符合條件之房間 ]
選擇房間
取回房間物件清單(房型
、起始時間和結束時間)
顯示房間與價格的清單
如果沒有符合條件
之房間,則重新輸
入或取消訂房
輸入姓名、身份證號碼
訂房
訂房(姓名、身份證號碼、房間號碼、起始時間、結束時間)
完成預訂//加入一筆訂房資訊
基本流程-順序圖
1: 初始化
2: 選擇(輸入)房型、起始時間和結束時間 6: 顯示房間與價格的清單
3: 查詢空房
7: 選擇房間
8: 輸入姓名、身份證號碼
9: 訂房
: 訂房邊界
類別
: 櫃臺人員
4: 查詢空房(房型、起始時間和結束時間)
10: 訂房(姓名、身份證號碼、房間號碼、起始時間、結束時間)
: 訂房控制
類別
: 資料庫控
制類別
5: 取回房間物件清單(房型、起始時間和結束時間)
11: 完成預訂//加入一筆訂房資訊
基本流程-合作圖
: 訂房控制類別
: 資料庫控制類
別
A : 房間
B : 時間範圍
C : 顧客
D : 訂房資訊
取得房間(房間號碼) //A
取回時間範圍(房間號碼、起始時間、結束時間) //B
加入時間範圍(B)
設定狀態(預定狀態)
取回顧客(姓名、身份證號碼) //C
[顧客資料存在]
[create/new object] 建立訂房資訊 //D
如果顧客資料
未建檔,則進
入建檔程序
設定訂房日期
加入房間(A)
加入訂房資訊(D)
完成預訂(C, D)
訂房操作細節
訂房操作細節-順序圖
1:取回房間 (房間號碼) //A
2: 取回時間範圍(房間號碼、起始時間、結束時間) //B
5: 取回顧客(姓名、身份證號碼) //C
10: 完成預訂(C, D)
: 資料庫控
制類別
A : 房間
3: 加入時間範圍(B)
4: 設定狀態(Integer)
: 訂房控制
類別
B : 時間
範圍
6: [create / new object]建立訂房資訊 //D
8: 加入房間(A)
7: 設定訂房日期
9: 加入訂房資訊(D)
C : 顧客
D : 訂房
資訊
訂房操作細節-合作圖
Case-Study:建構狀態圖

狀態圖只針對單一物件來描述

狀態圖非絕對必要:

除非… ”某類別的行為與狀態有相當程度的關
連性”

例如:”電話連線類別”,該類別”撥電話”、”
掛電話”等基本行為都跟電話的狀態有關(如:連
線狀態、響鈴狀態) 。
資料庫控制類別 - 狀態圖
初始化
do/ 建立資料庫連線
連線
程式結束
關閉
do/ 關閉資料庫連線
包含了所有資料
庫存取的動作
資料查詢或更新
資料存取
do / 資料查詢或更新
event[資料存取錯誤(例外)]/ 進入關閉狀態
exit/ 回到連線狀態
Case-Study:建構詳細的類別模型




決定類別行為
加入設計階段的類別
決定可見度(Visibility)
決定資料型態
決定類別行為

責任導向設計(Responsibility-Driven
Design)

訊息接收端(服務端)負責提供該訊息的服務
訊息M
客戶端:類別A
類別A
服務端:類別B
類別B
訊息M()
(Cont.)

互動圖提供了大部分的訊息資訊


通常直接把互動圖中的訊息當成類別行為
互動圖愈精密,捕捉到的類別行為也就愈多
訂房邊界類別
訂房控制類別
初始化()
選擇(輸入)房型、起始時間和結束時間()
查詢空房()
顯示房間與價格的清單()
顯示房間、價格()
輸入姓名、身份證號碼()
訂房()
查詢空房(房型、起始時間和結束時間)
訂房(姓名、身份證號碼、房間號碼、起始時間、結束時間)
資料庫控制類別
取回房間物件清單(房型、起始時間和結束時間)
完成預訂()
取得房間(房間號碼)
取回時間範圍(房間號碼、起始時間、結束時間)
取回顧客(姓名、身份證號碼)
顧客
姓名
身份證號碼
性別
出生日期
電話
住址
訂房資訊
預定 訂房日期
1 0..* 設定訂房日期 () 0..1
加入房間(房間 )
房間
房間號碼
1..*
1
加入訂房資訊(訂房資訊)
0..*
房型
價格
住房人數
景觀
訂房程序相關類別圖(1)
加入時間範圍(時間範圍)
1
0..*
時間範圍
起始時間
結束時間
房間狀態
設定狀態(房間狀態)
(Cont.)

資訊隱藏(Information Hiding)

保護物件內部資料(屬性)


為資料提供公開的存取介面(方法)


Java/C++之private修飾子
Java/C++之public修飾子
例如:訂房資訊類別(訂房程序相關類別圖(1))中
的訂房日期應該受到保護,並提供一組存
取方法,分別是”設定訂房日期”以及”
取出訂房日期” 。
(Cont.)

基於“資訊隱藏”的概念,可再次捕捉到
許多行為! 如下圖-訂房程序相關類別圖(2)

Note:訂房程序相關類別圖(2)中,只列出部
分屬性對應的方法。
訂房邊界類別
訂房控制類別
初始化()
選擇(輸入)房型、起始時間和結束時間()
查詢空房()
顯示房間與價格的清單()
顯示房間、價格()
輸入姓名、身份證號碼()
訂房()
查詢空房(房型、起始時間和結束時間)
訂房(姓名、身份證號碼、房間號碼、起始時間、結束時間)
資料庫控制類別
取回房間物件清單(房型、起始時間和結束時間)
完成預訂()
取得房間(房間號碼)
取回時間範圍(房間號碼、起始時間、結束時間)
取回顧客(姓名、身份證號碼)
顧客
姓名
身份證號碼
性別
出生日期
電話
住址
房間
房間號碼
預定
加入訂房資訊 ( 訂房資訊) 1
設定姓名 ()
取得姓名 ()
設定身份證號碼 ()
取得身份證號碼 ()
...略()
訂房資訊
訂房日期
設定訂房日期() 0..1
0..* 取得訂房日期()
加入房間(房間 )
訂房程序相關類別圖(2)
房型
價格
住房人數
景觀
...略()
加入時間範圍(時間範圍)
設定房間號碼()
1..* 取得房間號碼()
1
0..*
1
0..*
時間範圍
起始時間
結束時間
房間狀態
設定狀態(房間狀態)
取得狀態()
...略()
加入設計階段的類別

設計階段的容器類別



如:Array、List、Vector...
必須在OOD階段被考慮進來
例如:

”訂房控制類別”向”資料庫控制類別”要求
取回房間物件清單,此”清單”便是設計時期
需要的類別,它可以是Array、List或Vector。
(Cont.)

對於這類的結構,在此一律以List來表示

List所包含的資料項目通常是物件參考或識別碼
List
訂房邊界類別
add()
remove()
get()
size()
clear()
... 略 ()
初始化()
選擇(輸入)房型、起始時間和結束時間()
查詢空房()
顯示房間與價格的清單()
顯示房間、價格()
輸入姓名、身份證號碼()
訂房()
資料庫控制類別
訂房控制類別
查詢空房(房型、起始時間和結束時間)
訂房(姓名、身份證號碼、房間號碼、起始時間、結束時間)
取回房間物件清單(房型、起始時間和結束時間)
完成預訂()
取得房間(房間號碼)
取回時間範圍(房間號碼、起始時間、結束時間)
取回顧客(姓名、身份證號碼)
加入容器類別
(Cont.)

考慮應用程式的起始點:

在此補上一邊界類別-”訂房系統應用程式”,
該類別為應用程式的起始點

以Java而言,此類別中包含了main方法
訂房系統應用
程式
+ 啟動()
+ 顯示主畫面()
0..1
開啟
1
訂房邊界類別
0..1
0..1
0..1
開啟
開啟
1
取消訂房邊界類別
開啟
1
Check-in邊界類別
加入系統起始類別
1
Check-out邊界類別
(Cont.)

考慮多重性(Multiplicity)

例如:”顧客”可以有多筆”訂房資訊”,而每筆”
訂房資訊”則只歸屬於一個”顧客”。此時通常讓顧
客保有一份”訂房資訊清單”,而訂房資訊則保有可
參考到顧客的屬性 。
顧客
姓名
身份證號碼
性別
出生日期
電話
住址
訂房資訊清單 : List
加入訂房資訊 ( 訂房資訊 )
設定姓名 ()
取得姓名 ()
設定身份證號碼 ()
取得身份證號碼 ()
...略 ()
預定
1
訂房資訊
訂房資訊編號
訂房日期
顧客身份證號碼
0..* 設定訂房日期()
取得訂房日期()
加入房間( 房間)
顧客
姓名
身份證號碼
性別
出生日期
電話
住址
訂房資訊清單 : List
加入訂房資訊 ( 訂房資訊 )
設定姓名 ()
取得姓名 ()
設定身份證號碼 ()
取得身份證號碼 ()
...略 ()
房間
房間號碼
房型編號
時間範圍清單 : List
加入時間範圍 ( 時間範圍)
設定房間號碼()
1..* 取得房間號碼()
0..*
設定訂房日期() 0..1
取得訂房日期()
1
加入房間( 房間)
0..*
0..*
時間範圍
1
房型
起始時間
房型編號
結束時間
價格
房間狀態
住房人數
房間號碼
景觀
預定
1
訂房資訊
訂房資訊編號
訂房日期
顧客身份證號碼
房間清單 : List
...略()
設定狀態(房間狀態)
取得狀態()
...略()
決定可見度(Visibility)

軟體組成(單元):


屬性、行為、類別、套件…等
某個軟體組成之可見度,決定了該軟體組
成是否可被其他軟體組成所存取。
常見的可見度分類(以Java為例)

private


public


方法通常宣告成public
protected


屬性通常宣告成private
當類別本身落在某個繼承體系,有時為了提供子類
別存取權限,屬性/方法的可見度可能設定成
protected
friendly/package

只能被相同套件中的其他成員存取
公開(public)等級以’+’來表示,’-’則為私有(private)等級
顧客
-
姓名
身份證號碼
性別
出生日期
電話
住址
訂房資訊清單 : List
房間
訂房資訊
訂房資訊編號
訂房日期
顧客身份證號碼
房間清單 : List
- 房間號碼
- 房型編號
- 時間範圍清單: List
+ 加入時間範圍(時間範圍)
預定 + 設定房間號碼()
1..* + 取得房間號碼()
1 0..*
+ 加入訂房資訊( 訂房資訊)
+ 設定訂房日期() 0..1
+ 設定姓名()
+ 取得訂房日期()
1
+ 取得姓名()
+ 加入房間 (房間)
0..*
+ 設定身份證號碼()
0..*
+ 取得身份證號碼()
房型
時間範圍
1
+ ...略()
- 房型編號
- 起始時間
- 價格
- 結束時間
- 住房人數
- 房間狀態
- 景觀
- 房間號碼
+ ...略()
加入可見度後的類別圖
+ 設定狀態(房間狀態)
+ 取得狀態()
+ ...略()
決定資料型態

目的:

決定屬性資料型態


例如:整數(integer)、字串(String)
決定行為的原型定義

傳入的參數列(Parameter List)以及回傳值型態
(Note:若屬性具有初始值,此時可一併設定)
顧客
-
姓名 : String
身份證號碼 : String
性別 : Boolean
出生日期 : Date
電話 : String
住址 : String
訂房資訊清單: List
訂房資訊
- 訂房資訊編號: Integer
- 訂房日期: Date
預定 - 顧客身份證號碼: String
- 房間清單: List
1 0..*
+ 加入訂房資訊( orderInfo : 訂房資訊) : Void
+設定訂房日期(date : Date) : Void
+ 設定姓名(name : String) : Void
+取得訂房日期() :Date
+ 取得姓名() : String
+加入房間(room : 房間) : Void
+ 設定身份證號碼(id : String) : Void
0..1
+ 取得身份證號碼() : String
+ ...略()
1..*
房間
- 房間號碼 : Integer
- 房型編號 : Integer
- 時間範圍清單: List
+ 加入時間範圍( timeInterval: 時間範圍) : Void
+ 設定房間號碼(number : Integer) : Void
+ 取得房間號碼() : Integer
0..*
房型
- 房型編號: Integer
- 價格 : Float
- 住房人數: Integer
- 景觀 : Integer
+ ...略()
加入資料型態後的類別圖
1
1
0..*
時間範圍
- 起始時間: Date
- 結束時間: Date
- 房間狀態: Integer
- 房間號碼: Integer
+ 設定狀態(state : Integer) :Void
+ 取得狀態() :Integer
+ ...略()
撰寫虛擬程式碼

目的:讓後續的實作更有所依據

通常只挑選具代表性的或是比較複雜的程
序來撰寫。

撰寫語言:
自然語言
 程式描述語言(Program Description Language)
(Note:應避免直接用特定的程式語言)

類別名稱:訂房控制類別
方法(操作):訂房
輸入:name:String, id:String, roomNumber:Integer, startDate:Date, endDate:Date
輸出:None (Void)
功能描述:完成單筆訂房資訊登錄的動作,而相關的資訊會反應到資料庫中。
void order (name:String, id:String, roomNumber:Integer, startDate:Date, endDate:Date)
{
房間 room = 資料庫控制類別.取回房間(roomNumber);
時間範圍 timeInterval = 資料庫控制類別.取回時間範圍(roomNumber, startDate, endDate);
room.加入時間範圍(timeInterval);
timeInterval.設定狀態(訂房狀態);
//訂房狀態的型態為Integer
顧客 customer =資料庫控制類別.取回顧客(name, id);
if (customer exists)
//not null
{
訂房資訊 orderInfo = new 訂房資訊();
orderInfo.加入房間(room);
customer.加入訂房資訊(orderInfo);
資料庫控制類別.完成預定(customer, orderInfo);
}
else
{
//進入顧客建檔程序 (略)
}
}
總結

本章簡單呈現了OOD時期常見的工作,從
類別的規劃、動態模型的建構(互動圖、狀
態圖)、詳細類別模型(設計模型)的成形,
到虛擬程式碼的撰寫…等。

Note:使用者介面以及資料庫的相關塑模
技巧屬於專門技術,而且通常跟特定的函
式庫或系統介面相關,並不在本課程涵蓋
範圍內。
(Cont.)

最後要再重複強調的是:

不管分析或設計,免不了重覆往返的過程。

適度的往返修改,是軟體發展過程中的常態
(尤其是針對中大型的軟體系統)

如何讓往返修改的錯誤與負擔達到最低,這跟
個人經驗有關,同時也是學方法(論)的目的。
The End