第八章分析與設計階段– 物件導向設計(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