最近看到有人領域模型中的客戶類別如此設計:
這個設計觀點是將個人戶與公司戶都看成客戶,因且可以把公司戶與個人戶的都有的共通屬性放到客戶類別上,然後把個別所特有的差異化屬性放到衍生類別個人與公司上。表面上,這似乎是Party〔Fowler96〕樣式的應用,然而仔細推究其設計內容,卻會發現這個設計會使系統增加沒有必要的複雜度(Unnecessary Complex)。
因為個人戶與公司戶其實有很大的差異性,衍生類別的屬性很多,也有複雜的處理邏輯,就算我們採用的永續機制框架-Hibernate可以處理類別的繼承,但繼承卻讓我們的類別之間相互牽連,卻只是為了讓公司與個人的屬性不要重覆出現而要花費如此的成本似乎太奢侈了。
其實這個設計最大的問題是只從結構面的角度來設計類別,只考慮抽取共通屬性,卻缺少了行為面的封裝,這是營養不良的領域模型,設計的本質還是採用程序導向的基本思維,造成問題的主因是設計者並未針對實際發生的問題層面來設計,而是以預期的技術層面來考量,也就是說,樣式的錯誤引用把設計問題變複雜了。
為什麼說是樣式的錯誤引用呢?Martin Fowler(1996)曾提過,Party〔Fowler96〕樣式有兩種使用場合:
- 在領域模型中看到人與組織具有共同的行為,或~
- 不需要區分人與組織,在這種狀況下,可以不提供衍生類別。
由上可知,這個設計並不適用Party[Fowler96]樣式或使用這個樣式的時機並未來到,可以預期的是,少了行為的封裝,實作大致會變成像這樣:
if (cust instanceof Person.class) {
Person p = (Person) cust;
// process person customer ...
} else if (cust instanceof Company.class) {
Company c = (Company) cust;
// process company customer ...
}
可想而知,系統設計的複雜度會從此開始衍生下去,這將是我們所不願發生的事情,我的建議是把公司與個人的is-a關係拿掉,因為目前對於一般交易而言,客戶並不需要區分他是個人或是公司,如果需要知道客戶的細節資料時,我們只要提供一個根據客戶編號查詢個人或公司資訊的操作界面,然後在領域模型中將個人或是公司的類別須關聯至客戶類別,這樣我們就可以利用查詢條件的方式就可以找出關聯此客戶的個人或是公司,就可以透過此界面取得此客戶的詳細資料。
如此設計就可以變得更單純些,等到我們需要區分出不同的客戶的共通行為再運用角色塑模(Dealing with Roles)來增加設計的彈性,這就是用面對問題的方式來設計,而不是以技術實作的觀點來設計,因為後者常常會讓我們離遠使用者需求。
在與《類別設計演化論》相關主題的討論過程中,曾針對網友GameBoy所說的「運用 "is a" 和 "has a" 要能洞察 Problem domain(初學的人容易用 real world 直觀的認知來 model,這有點危險),甚至在某些場合併用更能達到 Model 中微妙的動態平衡發揮軟體的軟性」回應:
一味地求虛其實更危險,過度設計的結果是頑空!
實務上常碰到這種人,脫離業務需求,只鑽進沒有必要的設計。
is a 或 has a 並不重要,那只是手段而已。重要的是"面對現實",也就是思考這樣設計究竟要解決什麼問題。因此,用 real world 的直觀來 modeling 沒有什麼不好。
表面上, GameBoy 與我所說的並沒有太大的差異,但我卻不認為「用 real world 直觀的認知來 model,這有點危險」。因為,如果我們不能用 real world 的語言與別人溝通的話,就不可能站在需求的觀點來看事情,我們常常看見在開發過程中,開發人員往往以為只有他們才能了解真正的需求是什麼而造成溝通上的障礙,亦即:
誠如Robert C. Martin在《敏捷軟體開發》一書中提到過的:"程式常常會誘使你遠離樣式或原則"。同樣地,在設計過程中,不適當的設計樣式也常會誘使你遠離真正地業務需求.
樣式學得愈多的人,愈容易發生這種毛病。因此,更需要自我提醒. 不要用技術眼光來看業務需求, 因為那是一種以徧概全的徧見,後面必將自惡果。
所以,站在問題概念的層次上分析問題,而不是站在技術實現的層次上。從概念層次上進行分析,也就意味著要求你從領域專家的角度來看待程式是如何表示現實世界中的概念,然後針對能夠滿足現實世界觀介面設計(註)。
總之,面對現實才能發現問題的本質,因此設計必須要面對現實。尤其應該要認清不是不能用直觀的方式去看真實世界,而卻是往往我們用錯誤的實作方式而把設計變複雜,尤其是在應用某些樣式之前,必須謹慎,不要讓樣式的錯誤引用讓問題複雜化。





您好
我用ㄧ個例子來解釋
「用 real world 直觀的認知來 model,這有點危險」。
比如以 "汽車" 為例
"汽車" 在資產系統、或進銷存系統、或在某某系統
不一定都會被 model 成汽車
可能是 Item 或 Asset 或 anything else
也就是要看清楚 Problem Domain
(其實也就是您在本文的題旨)
會講出這樣的話
「用 real world 直觀的認知來 model,這有點危險」。
是看到有些初學者容易犯這樣的問題 (汽車不見得在業務需求裡都會被視為 "交通工具\")
但如果您就是認定敝人是以 Technical 的角度來看問題 (而非業務需求)
我想我也就是隨緣了
能在網上相遇自是有緣
如果我的文筆很容易造成您對我的誤解
That’s fine …
Have a nice year ….
GameBoy
敝人同樣也在 葉 Sir 另ㄧ篇 PO 文做了回應
http://www.lifeparty.idv.tw/bl.....8#comments
其實我常常拿 Agile Software Modeling 中的
Salary System 去考別人
大概十個人有九個人
會很直觀地用繼承來 Model Person 和 Role 的關係
(他們多半認為 Role is a Person 是很直觀的)
以該書該例的 reauirement 來看
這樣的繼承手法
自然會導出惡果
(但是在某些 requirement 來說
用 is-a 才符合 Business Semantic
或同時存在 is-a 和 has-a 的語意)
這是敝人提出這樣說法的起因
並非 Challenge 葉先生
葉先生提出
「用 real world 直觀的認知來 model,沒什麼不好」
在他本 PO 文來看
我個人解讀 他要強調的是
小心 Over Design
小心為 Pattern 而 Pattern
而敝人提出
「用 real world 直觀的認知來 model,這有點危險」
純粹只是從過去的經驗來提醒初學者罷了
文字有其侷限性
解讀因人而異
我個人
倒不會將
「用 real world 直觀的認知來 model,這有點危險」
和
「用 real world 直觀的認知來 model,沒什麼不好」
解讀為兩個衝突的觀點
(我個人解讀是兩人的出發點不太一樣)
因為葉 Sir 的直觀不會犯了以上過於 Technical 的問題
但往往我看到 初學 OO 的人卻常常因他們的過於直觀 而犯了這樣的問題
我到認為 葉 Sir 這篇 PO 文
對於初涉 OO 的人
是ㄧ個很好的活教材
供諸君作參考
因為我看到太多 beginner 的抽象化是
僅僅源自於他們的直觀認知
而非從 Requirement 下手
但 Real World 中直觀的名詞
不見得適合作為 Domain Model 中的 Entity
但卻往往被他們誤植於 Domain Model 中 …
有一位網友 John 提到
不妨看看 Evans 的書
我想他指的應該是這一本書
Domain Driven Design …
最近隨手翻翻
該書再提到
如何從與 User 的互動中
Iterative 地焠鍊出 Domain Model
有談及如何避免 用直觀的名詞來 Model 的 案例
初學 Modeling 的朋友
不妨做個參考
(我並非 Challenge 葉 Sir
只是我必須將敝人
「用 real world 直觀的認知來 model,這有點危險」
這句話交代清楚
)
供參考
Gameboy
想請教一下同人
敝人常用的 "結構" 一詞
在敝人的認知裡是包含語意這部份的
(也就是 model 要能呈現 problem domain 中的 Business Semantic
像 domain class 之間的 association
應該要能呈現豐富的語意
)
您對 "結構" 一詞
應該有比較嚴謹的定義
從您的 po 文看來
結構 和 Semantic 似乎是分開來的 (也許是敝人對您的文字解讀有誤)
若您有空針對此問題回 po
願聞其詳
Thx
[...] 在〈軟體設計需面對現實〉一文中,我曾針對 Gameboy 提出「用 real world 的直觀來認知 model ,這有點危險」提出個人的看法。對此 Gameboy 為其觀點 defend,然而卻在討論過程中卻因為對文字感受不同而造成一些爭論。因此,我認為有必要再進一步地探討「用 real world 的直觀來認知 model 」這個主題,以避免不必要的誤會。 [...]
[...] 我對所謂的 mix-in 並不熟悉,於是循線看了〈PHP 實踐 mix-in 概念之可行性〉,我發現石頭成所用的實作方式,類似 GOF Design pattern 的 State pattern,利用多型介面與自委託(self-delegate)技巧,可以讓物件同時具有多種型別的行為能力,甚至還可以動態改變物件的型別。 如此看來,石頭成對 Java 介面的負面觀感其實是出自於對 Java 介面使用的誤解。提高程式碼的重用性用 Java 技術不是只能靠繼承而已,繼承的誤用只會造成程式碼的僵化與脆弱而已,而實作介面來面對多重繼承的問題,那並沒有比誤用繼承高明到那裡!Java 或其它採用靜態型別的物件導向程式語言,解決一個實體有多種型別的問題,不是靠繼承樹或重覆實作介面,而是利用組合(Composite)角色子型別(Role subtype),即所謂的角色塑模(Dealing with role),介面的使用是用來除耦-讓設計與實作分開而不是用來當成類別多重繼承的替代方案。 [...]
[...] 對於同學的看法,我的觀點是改變軟體開發現況與面對現實其實是不相衝突的兩件事。創新的好處除了是為了創造更大利潤外,更重要的是如圖中的紅線所示,使競爭對手無法輕易模仿而減少競爭壓力,當愈沒有競爭壓力時,我們更有足夠的時間與空間可以發揮創新的做法而形成良性循環。而創新並不代表忽略現實,它是一種態度而非制式的特定作法,亦即面對問題對方法論的採用、調適與熟練三部曲。不要因為現實而忽略方法,也不要因為方法而昧於現實,徧向任何一端其實只是道德上或是情感上的無意義爭論。 [...]
[...] 運用 design pattern 雖然是增加設計彈性的利器,但為了維護這些設計彈性,卻必須付出開發成本,當我們的設計充斥了「沒有用的彈性」時,就會浪費許多無謂的時間與資源,而不會再有足夠時間回應軟體需求的變化,及足夠的空間來容納真正需要的功能。以經濟學的角度而言,設計者須重視設計的機會成本,要為現實需求而設計,避免想太多而造成軟體設計的過度工程化的後遺症,而在設計上充斥著沒有必要的複雜度。以物件導向設計原則而言,抽象性愈高的套件,穩定度就要愈低,否則它就會沒有什麼用處,穩定度愈高的元件,抽象性就要愈低,否則它就會很難被重複使用[1]。總之,要設計彈性與功能兼具的系統,其實最關鍵的是務實與務虛兩種設計觀點能夠相互達到均衡。 [...]
[...] 就拿本文的例子來說,一開始我們並不著眼於 design pattern 上,而是隨著設計不同著眼考量上,逐步發現並解決設計問題的過程中,讓 design pattern 躍然成形。讓問題來主導設計,而非讓設計使問題複雜化。當然,這也有賴於開發者在平常就要不斷地練習與思考,畢竟簡潔而富有彈性的設計不是一蹴可幾的呀。 [...]