天天看點

《軟體方法》第8章 分析 之 分析類圖(2)

8.1.6 識别分析類和屬性的要點

8.1.6.1 關于中英文命名

該用中文就用中文,該用英文就用英文,該用日文就用日文。中英文命名問題和設計工作流(編碼、設計資料庫……)中碰到的問題是類似的。分析類雖然不包含設計工作流的知識,但它是設計工作流的基礎。反對在設計工作流中使用中文命名(類名、屬性名、表名、字段名……)的理由可能是編譯器不支援、DBMS不支援、切換輸入法太麻煩、版式不好看等。編譯器、DBMS因素随着時代的發展慢慢地不再是問題,版式、切換輸入法問題在畫圖模組化中不存在,是以用中文名稱不是大問題。當然,如果開發團隊是國際化的,就是另外一種情況。

總之,分析類和屬性(包括後面要添加的操作)的名稱應該以友善開發團隊思考和交流領域知識為首要考慮因素。

EA提供了别名(Alias)。名稱可以用英文——其實不是英文,是編譯器廣泛支援的符号集合。除名稱外,再加一個别名用于顯示。不過,建議暫時不用。如果熟悉的領域詞彙是中文名,在那時還需要花時間去想一個英文名,分散了模組化中思考的精力,更不用說模糊的漢語拼音和錯誤的英文會給後續工作帶來的麻煩了。

8.1.6.2 命名中不帶備援資訊

不要在類名的最後加"類"字;不要在類名的前後加"Class"或"C";不要在類名的最後加"情況"、"資訊"、"記錄"、"資料"、"表"、"庫"、"單"等詞。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-47 類名後面不需要加備援詞

如果系統關注的焦點是"資料處理",處理的資料是什麼内容無所謂,"資訊"、"資料"也可以作為一個類的名稱,但它和"人員"不屬于一個領域,不在一個抽象級别。如圖8-47。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-48 "資訊"作為類

當然,如果一個帶有以上字尾的詞在領域中已經存在很久,成為了領域的術語,直接使用無妨。例如"訂單"帶有"單"字,實際上描述的是一次"購買",但"訂單"已經在領域中廣泛使用,可以作為類名。

屬性名稱前不需要加類名。

如圖8-49。按"類的屬性"念出來,"人員的姓名"很好,"人員的人員姓名"備援了。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-49 屬性名稱前不需要加類名

英文名用單數。

如圖8-50。"顧客"的執行個體是一名顧客,"顧客們"的執行個體是什麼?一個集合?

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-50 英文名用單數

設計工作流中的資料庫表名也應該用單數。有一些開發人員習慣在最後加s(當然,複數未必是加s),甚至有一些架構直接就在表名後面加上s,理由是表裡有很多行。這個問題和上面提到的類名稱後面加個"類"字是一樣的,都是一種備援。"類"、"表"的概念已經隐含了"多個對象"、"多行"的意思。說"我是一個類,我的名字叫顧客"就夠了,不需要說"我是一個類,我的名字叫顧客類"或者"我是一個表,我的名字叫顧客們"。

8.1.6.3 命名要一緻

同一個概念要用一緻的詞彙表達。例如"寶貝"還是"商品"?"顧客"、"客戶"還是"會員"?如果沒有差别,應該統一到同一個詞彙,如果有差别,應該在類圖上表達其中差别。如果還沒有思考到如何在類圖上表達,可以先在類的說明中闡述确切含義。

如圖8-51,左側的幾個概念中,"寶貝"和"商品"、"顧客"和"客戶"含義相同,把它們合并,同時在類圖上更精細地表達"使用者"、"顧客"、"會員"概念之間的差别。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-51 理清混淆的概念

8.1.6.4 切勿照貓畫虎

如果用例規約是完整的,從用例規約中找到實體類應該問題不大。不過,有的開發人員根本沒有寫用例規約,也不具備基本的對象模組化技能,會發現自己找不到合适的類甚至找不到類。

類圖長得像用例圖。

例如,一個電商系統,開發人員一開始心裡有一個想法,要做一個“搜尋商品”的功能。那麼要實作這個功能,需要什麼類呢?開發人員幹脆就按看到的表面現象來找類,得到的類圖長得非常像用例圖,如圖8-52。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-52 長得像用例圖的類圖

顯然,系統之是以能夠為顧客查詢“螢幕尺寸為4.6-5.0英寸的Android手機”,不是因為它記住了哪位顧客查詢過哪件商品,而是因為它記住了各種商品的類别和特征。正确的類圖應該類似于圖8-53。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-53 更合适的類圖

類長得像過程。

沒有基本的面向對象抽象思維的開發人員,可能會按照面向過程的思路噼裡啪啦把代碼寫出來,然後就感覺似乎不需要什麼類來幫忙了。即使出于趕時髦需要類,他就把過程名稱加上or或er作為類名稱,然後把原來的過程作為or或er類的操作,有時還用上一些泛化關系來做點綴,如圖8-54所示。這樣做表面上似乎是面向對象了,實際上是換湯不換藥,沒有得到面向對象的好處。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-54 僞面向對象抽象

碰到這種情況,可以思考如果按照面向過程的思路來編碼,什麼地方的代碼最複雜?長長的“算法”中定義的變量,往往就是候選的實體類。

類長得像報表。

在沒有用例規約的情況下,開發人員可能是根據手上的一些類似于報表的需求素材來找類。如果缺少抽象能力,就會把報表直接變成類,照抄報表的每一欄作為屬性,如圖8-55所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-55 長得像報表的類

報表隻是視圖,把更為本質的多個實體類的一些屬性組織在一起。針對這種情況,可以用下文介紹的類和屬性檢驗規則來深入思考背後的領域概念。

正如第一章所說,需求和設計不是一一對應的。設計源于需求,高于需求。

8.1.6.5 使用核心域術語

一個領域之是以能作為“領域”為人認知,必定存在一套日益完善和精确的術語體系。每個術語有其獨特的、其他領域術語不能替代的内涵。分析模型中的名稱應該來自核心域的術語體系。

模組化人員可能會用自己熟悉的領域的術語體系去代替不那麼熟悉的核心域術語體系,還引以為豪。例如,面對一段集裝箱領域裝箱規則的描述,模組化人員立即在大腦中把它轉換成自己熟悉的概念:棧、連結清單、樹……認為這是“透過現象看本質”,甚至宣稱“我就是程式,程式就是我”!

Fred Brooks在《人月神話》[Brooks 1995]中引用了James Coggins的一段話:

The problem is that programmers in O-O have been experimenting in incestuous applications and aiming low in abstraction, instead of high. For example, they have been building classes such as linked-list or set instead of classes such as user-interface or radiation beam or finite-element model.

問題是面向對象程式員在開發錯綜複雜的應用時,關注的是低層次,而不是高層次的抽象。例如,他們開發了很多像連結清單或集合這樣的類,而不是使用者界面、射線束或者有限元模型。

不同領域有不同的難題,因為覺得困難,是以對真正要解決的核心域問題視而不見,卻花精力去做那些自己熟悉的、他人已解決的其他領域問題,是一種逃避。

涉衆常使用的詞彙不一定是合格的領域術語。涉衆經常使用一些不嚴謹的稱呼,例如用顔色來表征:綠本(小産權證)、綠卡(永久居民卡)、綠單(預約單)。這些稱呼中的資訊是不穩定的,如果政府決定改用其他顔色,括号裡的概念不變,括号外的稱呼就得變化了,即使已經形成了習慣一直沿用下去,真實的内涵和字面的意思已經大相徑庭。

這樣的現象是正常的。正如第7章所說,涉衆關注的是涉衆利益,不關注系統需求,更不用說分析了。某類涉衆的領域知識可能會很片面,對領域概念認識不深刻。怎麼能寄望一個使用陌陌約會的屌絲青年清楚社交六度空間理論呢?

為了精确地使用領域術語,模組化人員需要同時精通兩方面的知識——領域知識和模組化知識,才有可能得到深刻反映領域内涵的分析模型。缺少領域知識的模組化好手固然比兩方面知識都不具備的小白要好,但得到好模型的最大障礙還是對領域知識的了解不足。

這時,模組化人員需要領域專家的幫助。幫助可以是直接的——模組化人員和領域專家一起工作;也可以是間接的——模組化人員閱讀專業書籍。模組化人員和領域專家兩個身份可以合一,模組化人員慢慢具備領域專家的能力,或者領域專家慢慢掌握模組化技能。至于哪條融合的路線更好,沒有标準的答案,應該視市場更需要 “懂軟體技術的醫學人士”還是“懂醫學技術的軟體人士”而定。

8.1.6.6 核心域透鏡

為了避免核心域概念被非核心域概念掩蓋,我們可以采用一種如圖8-56所示的“核心域透鏡”的思考方式,從核心域視角去看所有的事情。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-56 用核心域透鏡映射各種概念

例如,以“學習和考試”作為核心域,經過透鏡前後的概念對比如圖8-57。

原描述 映射後的核心域概念 原描述過去變體 原描述将來變體(猜想)
Powerpoint 示範工具 黑闆、玻璃幻燈片、賽璐珞幻燈片 全息
檢查IP位址 檢查重複聽課學生 看臉、看簽名

檢查新的協定位址

檢查大腦晶片辨別

點選“開始”按鈕 開始考試 觀察到考生開始書寫
向資料庫“答題”表添加一條答題記錄 答題 在答卷上塗黑一格

圖8-57 經過透鏡前後的概念對比

再以上文提煉的UMLChina系統類圖,即圖8-58為例,被圈住的部分屬于“發郵件”領域的概念。使用電子郵件的方式來通知學員舉辦公開課的資訊,隻是現在的手段。這個手段是變化的,過去也許是通過電話和傳真,将來也許是通過短信、QQ和微信。如果通過“舉辦公開課”的核心域透鏡映射,就可以得到“通知手段”、“通知”、“通知資訊”、“聯系方式”等相對于核心域更穩定的概念。這方面的改進會在下文的精化過程中進一步描述。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-58 目前的UMLChina類圖中的非核心域概念(被圈住部分)

核心域透鏡也有助于減少前文所說的開發人員迷戀“底層”的現象。例如要思考“電子郵件”的内涵,未必就是要思考SMTP、POP3/IMAP,從核心域透鏡的視角看,思考關系鍊、資訊載體等概念對開發好目前系統更有幫助。

8.1.6.7 屬性要直接描述類

類和屬性連在一起說"類的屬性",應該能直接說得通,否則類和屬性的搭配是不合适的。這個時候應該找到或建立合适的類,把該屬性移進去。“屬性要直接描述類”這個要求和關系資料庫的第三範式“任何非主屬性不依賴于其它非主屬性”相似。

例如圖8-59,“聯系人的組織名稱”中間隔了個“組織”,不能直接說通,需要添加一個“組織”類,把“名稱”挪過去。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-59 屬性要能直接描述類

在缺少抽象的“照貓畫虎”式模組化中,這種錯誤比較常見。例如,一張工作居住證上确實有該人員聘用機關名稱。模組化人員對照工作居住證,一項一項把它搬到類圖上的類中。

如果确定每個聯系人隻就職于一個組織,而且系統隻關注組織的名稱,可以将“名稱”合并到“聯系人”成為一個屬性“組織名稱”,如圖8-60。不過,如果以上的假設發生變化,這樣的做法應變成本很高。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-60 特定條件下可以簡化

要特别說明的是,習慣于關系資料庫模組化的模組化人員有時會犯這樣的錯誤,在一個類裡放上另外一個類的屬性作為“外鍵”。比如針對上面的例子,模組化人員會想:“聯系人”裡放“組織名稱”确實不合适,但是放個“組織編碼”作為外鍵總可以吧?其實也不可以。"組織編碼"是“組織”的屬性,是封裝在“組織”中的秘密,“聯系人”不應該擁有“組織”的屬性,它通過關聯擁有“組織”對象,通過通路“組織”對象公開的操作間接通路“組織”的屬性。

“聯系人”裡放“組織編碼”不合适,放一個無意義的辨別“組織ID”呢?同樣也不可以。對象有辨別,這是一個共識,而如何表達對象的辨別,這是另一個領域的知識,而且實作規律和核心域知識無關。分析類中不需要主鍵、外鍵屬性。

在設計工作流,需要把類圖映射到關系資料庫時,确實需要把"組織"表的主鍵(可能是"編碼"也可能是系統生成的代理主鍵)放在"聯系人"表中作為外鍵,但正如上文所說,這同樣是另一個領域的知識,而且映射規律和核心域知識無關。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-61 不需要外鍵屬性

下面我們用“屬性直接描述類”檢驗規則檢查一下UMLChina系統的例子:

(1)用例規約中提到“聯系人目前所在城市所屬分區與公開課舉辦城市所屬分區相同”——“聯系人”的“城市”的“分區”,需要如圖8-62分解。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-62 分離不直接描述類的屬性——UMLChina系統例1

(2)用例規約中“通知任務的建立人和建立時間”。實際上是“通知任務”的“建立事件”的“時間”、“通知任務”的“建立事件”的“建立人”,而這個建立人就是公司助理。此時應分解為三個類,考慮到一個通知任務隻有一個建立事件和它關聯,而且不考慮建立事件的其他屬性,可以把“建立”合并到“通知任務”中,如圖8-63。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-63 分離不直接描述類的屬性——UMLChina系統例2

有時,屬性不直接描述類的情況比較隐蔽。如圖8-58中的“公開課”類,說“公開課的主題”,“公開課的大綱”是可以的,但是執行個體化後會發現,很多“公開課”對象的“主題”和“大綱”是一樣的,不同“公開課”對象的不同屬性值主要展現在“開始日期”、“結束日期”和“城市”上。當發現針對一些屬性,有很多對象的值相同,而且這些屬性剛好組成了一個領域概念,應該分離出這個概念。如圖8-64,可以分離出“課程”,把這兩個屬性移到“課程”中。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-64 分離有大量相同值的屬性到另一個類——UMLChina系統例子3

這裡經常會有異議,認為“公開課”也應該有“主題”和“大綱”屬性,因為一門課程的主題和大綱會随時間變化,我們需要像快照一樣,複制課程當時的屬性值,記住在某時某地舉辦公開課時的主題和大綱,否則維護的資訊是不真實的。如果按照這樣的思路,和公開課關聯的所有類的屬性都需要複制到公開課中,因為聯系人的姓名、城市的名稱也會改變。再推下去,就需要一個巨大的類,把所有通過關聯線連在一起的類的所有屬性組合在一起。

應對這種情況的一種做法是針對特别需要關注的視圖另外加報表類,例如“公開課通知”,這個報表類和“公開課”、“課程”不關聯,而且對象的值不會變化。當然,如果一開始沒有建立某個視圖的報表,對象的值發生變化後,想從其他視圖推導出該視圖不一定行得通。

如圖8-65所示,報表ReportA、ReportB、ReportC、ReportD記錄了某個時間點A、B、C不同屬性組合的情況。假設現在需要一種新的報表,裡面包含屬性a2,想要追溯某個時間點上a2相關的記錄已經是不可能的。因為a2已經變化,其他報表沒有儲存下來。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-65 從不同的類提取屬性組成報表

但這樣的情況也是正常的,系統僅僅需要映射領域的一小部分知識。比起記住“某個姓名曾經在某個城市名稱上過課“來說,記住“某人曾經在某個城市上過課”更加本質。

如果要更充分地記錄曆史,可以針對“課程的主題和大綱發生變化”這個領域事實模組化,也就是說,為對象建立不同的版本,或者記錄對象所有的屬性值變化,如圖8-66。

《軟體方法》第8章 分析 之 分析類圖(2)
《軟體方法》第8章 分析 之 分析類圖(2)

圖8-66 跟蹤對象的屬性值變化

(3)如圖8-67所示,“發件郵箱”的“最小時間間隔”。這個“最小時間間隔”在很多“發件郵箱”對象中值相同,說明它其實不直接和具體每個發件郵箱相關,而是和發件郵箱的規格相關。例如,如果我們設定,針對1*3.com免費郵箱,發送郵件的最小時間間隔為70秒(通過了解該類郵箱的發件限制規則來推算這個時間值),那麼就算注冊了100個1*3.com免費郵箱,這個值都是一樣的。同樣,SMTP伺服器、POP3伺服器……等屬性的值,也隻和發件郵箱的規格相關,和具體的每個發件郵箱無關。應該分離出“發件郵箱規格”,和“發件郵箱”關聯。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-67 分離有大量相同值的屬性到另一個類——UMLChina系統例子4

前文提到有一種屬性是狀态屬性。針對狀态屬性的檢查原則剛好反過來——連在一起說"屬性的類"應該能直接說得通。例如,“待舉辦”的“公開課”、“已通知”的“聯系人”。嚴格來說,狀态屬性不是真正的屬性,最後應盡量用狀态機來封裝狀态轉換的邏輯,然後删除狀态屬性。後文會專門闡述。

8.1.6.8 屬性在本領域内不可以再分解

複雜屬性

如果屬性再分解就得到其他領域的概念,那麼這個屬性可以留在類中。如果可以繼續分解成本領域的概念,可以考慮把這個屬性獨立出去變成另一個類。

如圖8-68中,"聯系人"的"稱呼"屬性的類型是String。String屬于基礎語義領域,已經不屬于人員管理領域,那麼"稱呼"可以留在“聯系人”中作為屬性存在,而"組織"還可以像右側所示分解成“名稱”、“辦公位址”等,這些概念依然屬于人員管理領域,是以可以考慮将“組織”分離為一個類,“聯系人”關聯到“組織”。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-68 分離可以在本領域内分解的屬性

注意:分離或不分離的理由是“是否另一個領域”而不是“是否簡單”。就拿“稱呼”分離到String來說,String其實不簡單。以.NET Framework 4.5為例,其中的String類有123個操作,遠遠超過人員管理領域程式員編寫的某個類所擁有的操作。

多重性大于1的屬性

另一種可以再分解的形式是多重性大于1的屬性。例如,一個人員會有多個電話,如果放在“人員”類中作為“電話1”、“電話2”、“電話3”……屬性,該類的很多對象的“電話3”屬性可能會出現空值,或者放不下更多的電話号碼,還有人會模組化成一個“電話”屬性,屬性值裡用逗号分隔各個電話号碼,這樣也模糊了屬性的含義。此時應該把多重性大于1的屬性分離到另一個類,設定要進一步抽象,如圖8-69。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-69 分離多重性大于1的屬性

有的模組化人員會在“人員”裡放上一個數組或清單,這樣做也是不對的。人員有多個電子郵件,這是一個領域的知識;用程式設計語言如何實作一對多的關聯,是另一個領域的知識。

8.1.6.9 屬性對所有對象都有意義

前文說到“類的屬性”的檢驗規則如果說不通,那麼類和屬性放在一起是不合适的,但這隻是必要條件,不是充分條件,即使說得通也未必合适。如圖8-67,人的姓名,人的▲▲(▲▲是男性特有的器官),人的〇〇(〇〇是女性特有的器官)好像都說得通,但如果問:是不是所有對象都應該有這個屬性呢?得到的答案就不同了。

是不是所有人都應該有姓名——是。

是不是所有人都應該有▲▲——不是,隻有一部分人有。

是不是所有人都應該有〇〇——不是,隻有一部分人有。

說明“人”發生了分裂,分裂成“男人”、“女人”兩個子集(子類)。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-67 分解隻屬于部分對象的屬性到子類

《軟體方法》第8章 分析 之 分析類圖(2)

掃碼或通路http://www.umlchina.com/book/quiz8_1_2.htm完成線上測試,做到全對以獲得答案。

《軟體方法》第8章 分析 之 分析類圖(2)

1. 為什麼面向對象分析設計方法比面向過程好?

 A) 面向對象更适合人腦去把握系統的複雜性

 B) 面向對象和需求的映射更直接

 C) 面向對象方法更容易掌握

 D) 面向對象更符合計算機的底層

2. 以下給類和屬性命名,最合理的是__________

《軟體方法》第8章 分析 之 分析類圖(2)
   B 
《軟體方法》第8章 分析 之 分析類圖(2)
   C 
《軟體方法》第8章 分析 之 分析類圖(2)
   D 
《軟體方法》第8章 分析 之 分析類圖(2)

3. 以下說法正确的有(多選):

A 實體-關系圖和資料流圖也可以描述分析模型

B 和設計工作流的對象相比較,分析工作流的對象的特點是僅存在于記憶體中,不儲存到硬碟

C 每個用例映射一個分析邊界類

D 識别分析類時,精力應該重點放在實體類上

E 識别分析類時,類名稱以涉衆常用的稱呼為準

F 系統外部有執行者,使用面向對象方法分析,系統内部一定有相應的實體類

4. 鐵路售票處,售票員使用售票系統來售票,在用例進行過程中,系統需要不斷向旅客回報車次、車票和價格資訊,系統還需要和銀行系統互動。"售票"用例的分析序列圖中,會出現_____個邊界類,_____個控制類,_____個實體類。

 A) 1,2,3

 B) 3,1,2

 C) 不定,1,3

 D) 3,1,不定

 E) 3,2,3

 F) 3,1,3

 G) 不定,1,不定

 H) 3,3,3

5. 從以下用例規約抽取類,哪些類應該抽取出來?

遊客選擇航線、航期,

系統回報該航期的剩餘倉位。

遊客選擇倉位所在層,

系統回報該層平面圖。

遊客選擇倉位,

系統驗證該倉位可以預訂,

系統儲存倉位預訂資訊,

系統回報預訂成功。

A) 層

B) 倉位儲存

C) 航線

D) 倉位驗證

E) 系統

 F) 倉位

6. 當我們把待開發系統稱為“系統”時,說明我們在思考________問題:

A 業務模組化

B 需求

C 分析

D 設計

7. 當我們把待開發系統稱為“UMLChina系統”時,說明我們在思考________問題:

A 業務模組化

B 需求

C 分析

D 設計

8. 要實作驗鈔機的“驗鈔”功能,恰當的抽象是?

A) 

《軟體方法》第8章 分析 之 分析類圖(2)
B) 
《軟體方法》第8章 分析 之 分析類圖(2)
C) 
《軟體方法》第8章 分析 之 分析類圖(2)
D) 
《軟體方法》第8章 分析 之 分析類圖(2)

8.2 步驟3-2 識别類之間的關系

目前我們已經得到的工作成果是圖8-46。接下來,我們開始講解如何識别類之間的關系。

首先要說明的是:先識别類和屬性、再識别類之間的關系這個思考順序隻是一個微小的思考周期内的順序,而要模組化一張類圖,需要很多個思考周期。也就是說,識别類和屬性→識别類之間的關系→識别類和屬性→識别類之間的關系→……是交錯進行的。閱讀用例規約或其他素材,一邊思考一邊模組化,不管識别出類、屬性還是關系,畫上去就是,并不需要假裝看不見類的關系先隻識别類和屬性,等畫完了類和屬性再識别類之間的關系。

類之間的關系有三種:泛化(Generalization)、關聯(Association)和依賴(Dependency)。依賴是一個大雜燴。可以這樣認為,如果B變化,A也需要變化,但A和B之間沒有泛化或關聯關系,那麼A依賴于B。

8.2.1 泛化和關聯的差別

泛化和關聯是面向對象的兩種基本複用機制。在泛化關系中,子類通過繼承超類而擁有超類的特征;在關聯關系中,對象通過組裝其他對象而擁有其他對象的特征。如圖8-68所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-68 面向對象的兩種基本複用機制

泛化表示集合關系,兩個類形成泛化,意味着超類的對象集合包含了子類的對象集合;而關聯表示個體關系,兩個類形成關聯,意味着一個類的對象個體組裝了另一個類的對象個體。如圖8-69所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-69 泛化和關聯的本質差別

集合關系還是個體關系,這是泛化和關聯的本質差別。僅僅從自然語言的表達來推斷,很多時候是不可靠的。

例如,自然語言"人有男有女"說的是泛化關系。因為意思不是一個人的個體組裝了若幹男人個體和若幹女人個體,而是說人的集合包含了男人的集合和女人的集合。但是,自然語言"人有手有腳"說的卻是關聯關系。因為意思不是人的集合和手、腳的集合有包含關系,而是說一個人的個體組裝了若幹手和腳的個體。您可以自行體會一下“人有車有房”和“人有高富帥有屌絲”的差別。

對于比較熟悉的領域,例如剛才的男女手腳,拍腦袋就可以知道是泛化還是關聯,那拍腦袋就可以了。如果進入陌生的領域,有時回溯到集合和個體的本質差別是必要的。下面列舉一些錯例參考。

泛化被誤作關聯的情況是比較多的:

圖8-70中,“員工有排程員、裝卸工、配貨員”指的是員工的對象集合包含了排程員、裝卸工、配貨員的對象集合,不是指一個員工對象由排程員對象、裝卸工對象、配貨員對象構成。正确的關系是右側的泛化關系,而不是左側的關聯(此處是組合)關系。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-70 泛化被誤作關聯 例1

很多系統經常需要設定一些參數,有人會把參數模組化成圖8-71左側的類圖,把逾時時間、鎖定設定、頻帶等作為參數的屬性。屬性其實就是關聯(此處是組合)的一種變體,8-71左側和右側是等同的。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-71 泛化被誤作關聯 例2

圖8-71的意思是一個參數個體由若幹個具體參數個體組成,這不符合領域内涵。更符合領域内涵的是“具體參數是參數的一種”或者“參數的集合包含各具體參數的集合”,也就是說,泛化關系更合适。還有一種做法是把具體的參數全部抽象為“名稱”和“值”兩個屬性。如圖8-72。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-72泛化被誤作關聯 例2 更正

如果按圖8-71的方式模組化,參數類隻有一個對象,但這個對象有很多個屬性。當需要為系統設定一種新的參數時,就需要修改類結構,增加新的屬性。如果按圖8-72的方式模組化,隻需要增加新的參數對象即可,類結構不需要改變。

一些1對0..1的關聯,有可能是泛化關系。例如,有人認為1台電器可以是1台洗衣機,也可以不是;1台電器可以是1台電視機,也可以不是;1台電器可以是1台空調,也可以不是,于是畫出圖8-73。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-73的意思是一台電器可能由一台洗衣機、一台電視機、一台空調組裝而成,這是錯誤的,應該是電器的集合包含洗衣機、電視機和空調的集合,即泛化關系。如圖8-74。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-74 泛化被誤作關聯 例3 更正

關聯被誤作泛化的情況:

幾個類擁有相同的部分時,有人可能會把相同的部分變成超類,和這幾個類形成泛化關系。如圖8-75,經理、組長群組員都有賬戶,于是把賬戶提升為超類,意思是“經理是賬戶的一種”或“賬戶的集合包含經理的集合”,這是錯誤的。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-75 關聯被誤作泛化 例1

經理和賬戶的正确關系應該是關聯(此處是組合),即使有泛化關系,也應該抽象出更合适的領域概念,例如“人員”,如圖8-76。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-76 關聯被誤作泛化 例1 更正

圖8-77是Meilir Page-Jones在他的書中舉的一個極端的例子。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-77關聯被誤作泛化及更正 例2

8.2.2 識别泛化關系

8.2.2.1 識别泛化的思路

直接形成

首先,類圖中的兩個類可能會直接形成泛化關系,如圖8-78所示。嚴格的做法是針對每兩個類,思考“A是B的一種嗎?”,再反過來思考“B是A的一種嗎?”不過如果真的要這樣做,工作量還是挺大的。類圖中有n個類,就需要思考2C2 n=n(n-1)次。n=11時,就是100次了!實際工作中,往往是先掃描一遍,大腦迅速過濾出可能值得這樣思考的類,針對這些類思考即可。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-78 直接形成-兩個類之間直接形成泛化關系

像圖8-78這樣,類圖上已有的兩個類有泛化關系但未識别的情況并不多,因為之前從用例規約識别類和屬性時很有可能已經發現了。

自下而上(從特殊到一般)

更多的情況是發現類圖上已有的兩個或多個類有共同特征,于是抽象出共同的超類,如圖8-79所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-79 自下而上-兩個類之上有共同的超類

以UMLChina案例項目的領域為例,可能會存在如圖8-80的自下而上的識别過程:

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-80 自下而上識别泛化 例子

關聯實際上就是擴充的屬性,如果多個類關聯到同一個類,也可以考慮泛化出共同的超類。

自上而下(從一般到特殊)

如圖8-81所示,這個識别思路就是“8.1.6.8 屬性對所有對象都有意義”裡的思路,此處就不再重複叙述。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-81 自上而下-一個類分裂出子類

人類認識世界的過程就是自上而下(從一般到特殊)的過程。例如對生物的認識,原始人的概念很簡陋,第一部辭書《爾雅》中已有簡單的分類:草木蟲魚鳥獸,今天的生物分類學按域、界、門、綱、目、科、屬、種分層,已經達到一個龐大的數字。

8.2.2.2 泛化進一步讨論

8.2.2.2.1 Liskov替換原則相關問題

可能您已經從一些書上看到過如圖8-82的矩形和正方形問題,有時這個問題被換成橢圓和圓。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-82 被廣泛讨論的正方形矩形問題

圖8-82中,正方形是矩形的子類。按照設想,設定矩形的A邊長為4,再設定B邊長為5,此時求面積得到4×5=20,但如果正方形的面積是20,邊長應該是√20=2√5才對。

矩形和正方形的問題經常伴随着Liskov替換原則(LSP)的讨論。LSP是Barbara Liskov在1988年提出的關于基類型和子類型的原則[Liskov 1988],原文如圖8-83所示。Bertrand Meyer[Meyer 1997]使用契約的觀點解釋了為什麼正方形和矩形之間泛化關系違反了LSP:子類操作的後置條件弱于超類。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-83 Liskov替換原則的原文

關于如何糾正,也有很多方案,但大多數是從實作技巧的角度來解決問題。我們盡量從領域知識的角度看問題。

矩形的定義是:至少有三個内角是直角的四邊形。由此衍生的性質有:對邊平行且相等、對角線互相平分且相等、面積=長×寬……這些是矩形的共性,沒有問題。之是以出現沖突,是因為我們不知不覺地把常見矩形的特征(鄰邊可以不等長)當成了所有矩形的共性。更合理的泛化關系應該如圖8-84所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-84 更合适的矩形類層次結構

在圖8-84中,正方形不是自由矩形的一種。自由矩形、正方形和黃金分割矩形(邊長比為黃金分割比0.618····:1)等是互相不重疊的矩形子集(子類),而且各子集的并集等于超集(超類)。如果能遵循這樣的思路,那麼建立的泛化關系應該符合Liscov替換原則。

矩形對象的屬性是封裝的,外部調用者隻能通過公開的操作修改和通路屬性,如“設定A邊(a)”、“設定B邊(b)”、“計算面積()”等。

操作和屬性不一一對應也不應該一一對應,這是面向對象的優點。對于有些矩形來說,“設定A邊(a)”操作隻是給A邊長指派為a,而對另一些矩形來說,“設定A邊(a)”操作既修改了A邊長,也修改了B邊長,甚至有的矩形還不讓修改呢!

以更常見的“銀行賬戶”類來舉例更容易幫助了解。“銀行賬戶”類有一個屬性“餘額”,但對外不能提供“修改餘額(金額)”、“修改狀态(狀态)”等操作,應該提供的是“存款(金額)”、“取款(金額)”等。

當外部調用者向“銀行賬戶”對象發送“取款(2000)”消息時,引起“銀行賬戶”對象内部的變化不僅僅有(1)餘額減少了相應金額,可能還要有:

(2)減去0.1%的手續費;

(3)如果餘額已經低于某個設定值,某個在内部表示狀态的屬性值會改變;

(4)建立一個和該賬戶關聯的“交易”對象記錄取款的細節。

改進後的矩形類圖可以如圖8-85所示。超類中實作“求面積”,但不實作“設定A邊(a)”、“設定B邊(b)”的操作,留給子類來實作。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-85 改進後的矩形泛化類圖

或者如圖8-86,把超類“矩形”到底保留幾個邊長屬性以及如何求面積的實作下放到子類。這樣的處理相當于把鄰邊互有依賴的領域知識放在屬性中,而不是放在操作中。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-86 另一種矩形類圖

在日常應用中也有類似于矩形和正方形的情況。例如,會員的Email和QQ可以自由變化,但有一種會員,我們姑且稱為“騰訊專屬會員”,他的Email隻能用他QQ号下的QQ郵箱,Email和QQ兩個屬性之間有依賴,那麼圖8-87左側關系是不合适的,應該改為右側的關系。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-87 會員和騰訊專屬會員

8.2.2.2.2 盡量不要跨領域使用泛化關系

分析工作流的類模組化關注的是核心域概念及其關系,但有時候模組化人員會不自覺地引入非核心域的内容。例如,若幹學員組成小組,模組化人員想到了如何實作小組有多個學員的問題,決定用List來實作,于是有圖8-88。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-88的問題是把本來應該隐藏在背後的非核心域概念顯式引入到核心域類圖中。前文已經說過,域之間的映射往往是有規律的,即使實作時由于某種偏好就是要通過泛化來複用,也沒有必要逐一畫出來。當然,更合理的實作是通過關聯來複用List,如圖8-89所示。

《軟體方法》第8章 分析 之 分析類圖(2)

圖8-89 通過關聯來複用List

《軟體方法》第8章 分析 之 分析類圖(2)

繼續閱讀