物件導向分析、設計、與程式 - OOA/D/P
企業流程與軟體發展現況
企業流程是任何企業的資訊應用系統所要處理的主要對象,企業運用資訊系
統完成其應有的程序,以獲得其利益,如客戶下訂單及是一個例子。此過程在人
們看來應是極自然的事情,但是往往在資訊系統上卻非如此。傳統系統分析師在
分析系統時,總是千方百計地要把一些企業流程上的概念,如客戶、訂單、銷售
等概念徹底分解成資料庫表格中的欄位,結果像「客戶」這樣自然的概念,就變
成一堆欄位的組合。因而造成程式設計師在實作系統時,往往變成以欄位作為程
式設計的對象。我相信「一堆欄位的組合」絕對不會比「客戶」容易思考與理解
吧!尤其當現在企業流程日益複雜,那「一堆欄位的組合」可能代表著成千上萬
的欄位組合,我覺得我不是超人,無法完全掌握這些欄位,我也相信應該無人能
完全掌握。重點是,欄位的思考方式是極不自然且難以理解的;因為企業流程中
是以「客戶」、「訂單」、「銷售」等概念來進行的,絕對不是以欄位來進行。
相信沒有一個流程的作業人員會說他是以欄位的觀念來進行工作的。但是很可笑
的是,現在多數的資訊人員卻真得是以欄位來思考整個系統。
讓我們發揮想像力來思考一下,若是以欄位來思考系統會變得如何?第一個
問題恐怕就是難以理解系統。試想,若是我們老是在思考系統時,一直在想欄位
的對應,原本是個具有整體意義的概念被我們拆得支離破碎〈變成一堆欄位〉,
如此相信分析到最後,連我們自己也很難搞得清楚。結果就糊里糊塗地進入實作
階段,因而造成了實作的問題。怎麼說呢?因為在這種情況下,程式碼很容易就
以欄位為對象來撰寫,但是要知道,企業流程是由多個具有整體意義的概念,透
過一連串的規則而完成。這些概念在世界上差異不大,但規則會隨著時空環境而
改變,所以當企業流程改變時,這時以欄位為對象的程式碼根本就難以跟上,因
為所有的概念與規則早已糾結在一團,如何能更改?即使有人真的有耐心去更改
,因為系統的難以理解,即使改好了當時要的部分,但是有誰真能保證其他部分
沒有受傷到影響?
諸如此類的問題在現實中層出不窮,如此更別說系統再發展等的問題了。所
以讓我們來看看物件導向方法論是否能在這裡貢獻一些力量。
物件導向分析與設計(OOA/D)
回想上一段所提到的:傳統的分析設計方法很容易做出難以理解的系統,因
為那樣的思路是不自然的。物件導向方法論基本上就提供了一個非常自然的方式
讓我們可以用自然的方式思考與瞭解企業流程。在物件導向中,系統中本就存在
「客戶」、「訂單」、「銷售」等概念〈術語就是類別或物件〉,這些物件抽象
及表達現實世界中的概念,同時這些物件不僅是靜態地存在著,它們還是「活生
生的」東西,讓我們可以要求它們做某些動作。系統中所有的物件以一種精心設
計並且符合企業規則的關係來互動,使得一切事物就好像真實世界一般。企業流
程就在如此自然易於理解的方式下完成。
再者,由於物件本身就表達出了一個整體概念,任何關於一個「客戶」的資
訊都封裝在一個「客戶」物件中。還記得前面所提到概念比較不會變〈相信世界
上所有的訂購都有「訂單」這個概念吧!〉,而規則較易改變的現象嗎〈例如三
不五時來個促銷〉?由於物件導向在概念的整體性,所以物件導向系統就比傳統
系統更能適應現在多變的企業環境,且更易維護、修改、與發展了。因為一切都
是那麼自然。
物件導向程式(OOP)
也許物件導向方法較令人難以接受的是:物件導向並不保證導引出容易寫的
程式碼。而且物件導向的觀念也讓許多對結構化程式有經驗的程式設計師難以接
受。
在物件導向系統的設計與實作階段,我們必須具備對物件導向觀念的瞭解,
同時我們也必須熟練一些其他專家已經發展出來的問題解法(Design Pattern),
當然了!我們也必須選擇及熟練一個支援物件導向的程式語言。但是克服這些困
難是值得的,因為我們從此將獲得一個易於理解、維護、與擴充的系統,而這樣
一個系統所省下的成本,絕對比提升資訊人員物件導向觀念的成本還要划算的多
。同時物件導向系統擁有繼承的特性,所以並非所有人員都要熟悉高深的物件導
向觀念,系統的架構可由較具經驗的設計師完成,此架構即可用來規範全體人員
的程式碼,而較無經驗的程式員則可以負責填滿實作,如此則可以解決物件導程
式較難撰寫的問題。
結論
總之,物件導向方法讓我們可以用一種極為自然的方式來看待系統,我們對
系統所做的分析,產生出來的系統模型,都與真實的企業流程相互對應,如果我
們再多花一點想像力,許多在真實世界中抽象的概念也都可以用物件表達出來。
事實上,在物件導向分析實務中,發現隱藏的概念亦是非常重要的任務,由這一
點來看,物件導向同時也提供了一個讓我們驗證對系統的了解是否完整的機會。
此後,我們對系統的了解將是由許多完整的概念與規則所組成,再也不是那種支
離破碎的欄位組合。我們從此可以真正專心在企業流程上,研究出真正適當且符
合使用者需要的流程,降低系統在維護與再發展的成本,從而企業才真正能由軟
體系統獲得最大的利益。這麼多的好處,為什麼不做呢?
一個簡單的範例說明:
底下以一個非常簡單的例子說明。
假設我們要計算某一張訂單的應付總額,而此應付總額可能隨著不同的促銷
時段而定。例如在促銷期間一律打八折,而一般期間不優待。
若是以傳統寫法可能會如此(以下是虛擬碼):
<font face="'Courier New', Courier, monospace" size="2">function compute() {
if 一般期間 then
計算訂單的應付總額...
else if 折扣期間 then
計算訂單的應付總額...
計算訂單的實際應付總額...
}</font>
但是若我們再訂出更多的時段,每個時段有不同的折扣會如何?結果我們勢
必加入更多的if..else判斷式。不過,若是我們在每個折扣時段訂出更為複雜的
折扣方式時,這個判斷式將會愈來愈龐大而複雜。如果再將折扣的對象改為依產
品與時段而定的話,那麼修改的工作就複雜多了。
<font face="'Courier New', Courier, monospace" size="2">function compute() {
if 一般期間 then
計算訂單的應付總額...
else if 折扣期間1 then
計算訂單的應付總額...
計算訂單的實際應付總額...
else if 折扣期間2 then
計算訂單的應付總額...
計算訂單的實際應付總額...
...
}</font>
反觀物件導向方式會如何看這個問題?(以Java為例子)
類別定義:
我們先依企業流程將我們所注意的概念以類別(Class)表達出來。
// 定義一個抽象類別表達出訂單的概念,
// 同時用以規範其子代類別必須有責任計算其應付總額 - getPayment()。
<font face="'Courier New', Courier, monospace" size="2">abstract class Order {
abstract double getPayment();
}</font>
// GeneralOrder類別繼承自Order類別。
// 注意:此時GeneralOrder類別必須實作getPayment()方法,
// 否則根本無法編譯。
<font face="'Courier New', Courier, monospace" size="2">class GeneralOrder extends Order {
double getPayment() {
傳回一般期間的應付總額
}
}</font>
// SpecialOrder類別繼承自Order類別。
// 注意:此時SpecialOrder類別必須實作getPayment()方法,
// 否則根本無法編譯。
<font face="'Courier New', Courier, monospace" size="2">class SpecialOrder extends Order {
double getPayment() {
傳回折扣期間的應付總額
}
}
</font>
Client 端應用:
此處之Client不是指Client-Server架構下的Client,它只是單純地表示任何
可能使用我們前面的類別定義的潛在程式碼。
// 執行企業規則
<font face="'Courier New', Courier, monospace" size="2">void processBusinessRules() {
// 由於SpecialOrder與GeneralOrder皆是Order的子代類別,
// 所以SpecialOrder與GeneralOrder可以當作compute的參數。
// 這很像人類的觀念:一般訂單與特殊訂單都是訂單的一種。
if 一般期間 then
compute(new GeneralOrder());
else if 折扣期間 then
compute(new SpecialOrder());
....
}</font>
<font face="'Courier New', Courier, monospace" size="2">void compute(Order o) {
// 一律要求Order自己去計算應付總額
// 折扣的資訊已經存在於Order的子代類別中
o.getPayment();
}</font>
雖然物件導向看來使程式碼變多了,但是仔細看看,大部分的程式碼都是在
定義訂單的概念。反而是真正要計算應付總額的地方變得極為簡潔。(我們可以
與前面比較compute的差別)此後即使我們增加不同的時段,更改的地方只是多加
一些Order的子類別,並適當的傳入compute而已,這樣的改變並不會影響其他程
式碼。若是將折扣的對象改為依產品與時段而定的話,只要設計Product等一系
列的類別架構即可輕而易舉地解決。(因為訂單是由產品所組成的)
事實上,若是我們善用一些已被發展出來的設計樣式(Design Pattern)則可
以將Order的子類別物件的產生加以封裝及抽象化,降低客戶端程式與產生物件
程式碼的耦合度,如此將可保留物件產生的彈性,當我們所面臨的情況改變時,
加入新類別或修改舊的類別後,客戶端程式才不會受到影響,或將影響降至最低
。諸如此類的方式或手法,你可以參考設計樣式的專書,那裡會提供非常多的解
法,使得整個系統變得非常有彈性,易於維護。
這樣的做法是否非常符合人類的思路呢?因為企業流程是依照人類的思路所
構思出來的,而物件導向讓我們可以用人類的思路來思索企業流程,所以企業流
程與物件導向是個天生的絕佳組合。而正因為是絕佳組合,所以物件導向方法可
以輕易地追隨企業流程的變化。
沒有留言:
張貼留言