我們如何知道軟體設計的優劣呢?以下是一些拙劣設計的症狀,當軟體出現下面任何一種氣味時,就表明軟體正在腐化。
僵化性
僵化是指難以對軟體進行改動,即使是簡單的改動。如果單一的改動會導致有依賴關係的模組中的連鎖改動,那麼設計就是僵化的。必須要改動的模組越多,設計就越僵化。
大部分開發人員都遇到這樣的情況:他們對被要求進行乙個看似簡單的改動,當他實際進行改動時,才發現有許多改動帶來的影響自己並沒有**到。最後,改動所花費的時間要遠比初始估算長。他會重複軟體開發人員慣用的悲嘆:「它比我想象的要複雜得多!」
脆弱性
脆弱性是指,在進行乙個改動時,程式的許多地方就可能出現問題。常常是,出現新問題的地方與改動的地方並沒有概念上的關聯。要修正這些問題就又會引出新的問題,從而使軟體開發團隊就像乙隻不停追逐自己尾巴的狗一樣。
牢固性
牢固性是指,設計中包含了對其他系統有用的部分,但是要把這些部分從系統中分離出來需要的努力和風險是巨大的。這是一件令人遺憾的事,但卻是非常常見。
粘滯性
當面臨乙個改動時,開發人員常常會發現會有多種改動的方法。其中,一些會保持設計;而另外一些會破壞設計(也就是生硬的手法)。當那些可以保持系統設計 的方法比那些生硬手法更難應用時,就表明設計具有高的粘滯性。做錯誤的事情是容易的,但是做正確的事情卻很難。這樣就很難保持專案中的軟體設計。
不必要的複雜性
如果設計中包含當前沒有用的組成部分,它就含有不必要的複雜性。當開發人員**需求的變化,並在軟體中放置了處理潛在變化的**時,常常會出現這種情況。起初,這樣看起來是一件好事。畢竟,為將來的變化做準備會保持**的靈活性,而且可以避免以後再進行痛苦的改動。
糟糕的是,結果常常正好相反。為過多的可能性作準備,致使設計中含有絕不會用到的結構,從而變得混亂。一些準備也許會帶來回報,但是更多的不會。期間,設計揹負著這些不會用到的部分,使軟體變得複雜,而且難以理解。
不必要的重複
複製(copy)和貼上(paste)也許是有用的文字編輯(text-editing)操作,但是它們卻是災難性的**編輯(code-editing)操作。時常,軟體系統都是構建於眾多的重複**片斷之上。
當系統中有重複**時,對系統進行改動會變得困難。在乙個重複的**體中發現的錯誤必須要在每個重複體中一一修正。不過,由於每個重複體之間都有細微的差別,所以修正的方式也不總是相同的。
晦澀性
晦澀性是指,**模組難以理解。當開發人員最初編寫乙個模組時,**對於他們來說看起來也許是清晰的。這是由於他們使自己專注於**的編寫,並且他們對 於**非常熟識。在熟識減退以後,他們或許會回過頭來再去看那個模組,並想知道他們為什麼會編寫出如此糟糕的**。為了防止這種情況發生,開發人員必須要 站在**閱讀者的位置,共同努力對他們的**進行重構。
什麼激發了軟體的腐化?答案是需求的變化。由於需求沒有按照初始設計預見的方式進行變化,從而導致了設計的退化。通常,改動都很急迫,並且進行改動的開發人員對原始的設計思路並不熟識。因而,雖然對設計的改動可以工作,但是它卻以某種方式違反了原始的設計。隨著改動的不斷進行,這些違反不斷地積累,設計開始出現臭味。
然而,我們不能因為設計的退化而責怪需求的變化。作為開發人員,我們對需求變化有非常好的了解。事實上,我們中的大多數人都認識到需求是專案中最不穩定的因 素。如果我們的設計由於持續、大量的需求變化而失敗,那就表明我們的設計和實踐本身是有缺陷的。我們必須要設法找到一種方法,使得設計對於變化具有彈性, 並且應用一些實踐來防止設計腐化。
老闆給你的任務。。。。。。
老闆一大早就來找你,要你務必在三個星期內完成這樣乙個程式:從鍵盤讀入字元,並輸出到印表機。
你是乙個很有效率的開發人員,僅僅用了兩個星期就把程式完成了(copy v1):
void copy()
你把程式編譯好後,安裝在公司裡的234個工作站。你的程式執行良好,3個月內一點問題都沒有,於是同事都齊聲讚揚你,老闆也開始賞識你。你自己也開始飄飄然了。
需求在變化。。。。。。
三個月後的某天的某個上午,老闆又來找你,說有時希望能從紙帶讀入機讀入資訊。你咬牙切齒,翻著白眼。你想知道為何人們總是改變需求。你的程式不是為紙 帶讀入機設計的!你警告老闆,這樣的改變會破壞程式的優雅。不過老闆怒視了你一下,你又立刻低下了頭,開始想解決方案了。
因為程式已經 安裝到數百個工作站,你不能改變copy程式的介面。改變介面會導致長時間的重新編譯和重新測試。單單系統測試工程師就會痛恨你,更別提配置控制組的那7 個傢伙了。並且過程控制部門會用專門的一天時間來對所有呼叫了copy的模組進行各種各樣的**評審。但是這也難不到你,你巧妙地完成了任務(copy v2):
//remember to reset this flag
bool ptflag = false;
void void copy()
想讓copy程式從紙帶讀入機讀入資訊的呼叫者必須把ptflag設定為true,然後再呼叫copy時,它就能正確地從紙帶讀入機讀入資訊。一旦 copy呼叫返回,呼叫者必須重新設定ptflag,否則接下來的呼叫者就會錯誤地從紙帶讀入機而不是鍵盤讀入資訊。為了提醒程式設計師重設這個標誌,你增加 了乙個適當的注釋。
同樣,你的程式一發布,就獲得了好評。甚至比以前更成功,一大群渴望的程式設計師正在等待機會去使用它。生活是美好的。
得寸進尺。。。。。。
美好的日子過得總是太快,幾個禮拜後的那天早上老闆又來光顧你,他說:客戶有時希望copy程式可以輸出到紙帶穿孔機上。
客戶!他們總是毀壞你的設計。如果沒有客戶,編寫軟體會變得容易得多。
你再次警告老闆,如果繼續以這樣可怕的速度變更需求,那麼在年底前軟體就會變得難以維護了。老闆心照不宣地點點頭,接著告訴你無論如何都要進行這次改動。
這次的改動和上次相似,只不過需要另外乙個全域性變數,下面的程式展示了你努力後的卓越成果(copy v3):
//remember to reset these flags
bool ptflag = false;
bool punchflag = false;
void copy()
尤其讓你感到驕傲的是,你還記得去修改注釋。雖然,你對程式的結構開始變得搖搖欲墜感到擔心。任何對於輸入或者輸出裝置的再次變更肯定會迫使你對 while迴圈的條件判斷進行徹底的重新組織。但是畢竟你的程式還能正常工作。不過現在已經到達你承受的底線了,如果可惡的客戶再次通過改變需求來破壞你 的設計你就立刻走人。你下定了這個決心。
你的崩潰。。。。。。
很不幸,沒過兩個星期。那天早上你剛到辦公室還沒坐下,老闆又跑了進來,看他焦急的神態你猜得出他已經等了你3個小時了。老闆開門見山地說:客戶有時希望copy程式可以從檔案中輸入……
沒等他把話說完,你已經衝出了辦公室,消失在茫茫的晨曦當中。
2.1 運用物件導向設計原則設計copy程式
讓我們換個場景來處理上面的情況如何?~^_^~
1、 當老闆第一次給你任務時,你還沒預計到任何需求的變化,所以一開始編寫的**和「copy v1」完全一樣。
2、 在老闆要求你使程式可以從紙帶讀入機中讀入資訊時,你作出了下列的反應:
class reader;class keybordreader : public reader
}keybordreader gdefaultreader;
void copy(reader& reader = gdefaultreader)
3、 在老闆要求你使程式可以輸出到紙帶穿孔機時,你作出了下列的反應:
class reader;class keybordreader : public reader
}class writer
;class printerwriter : public writer
}keybordreader gdefaultreader;
printerwriter gdefaultwriter;
void copy(reader& reader = gdefaultreader, writer& writer)
在要實現新需求時,你抓住這次機會去改進設計,以便設計對於將來的同類變化具有彈性,而不是設法去給設計打補丁。從第一次改進開始,無論何時老闆要求一種 新的輸入裝置,你都能以不導致copy程式退化的方式作出響應;從第二次改進開始,無論何時老闆要求一種新的輸入或輸出裝置,你也能以不導致copy程式 退化的方式作出響應。
但請注意,你不是一開始設計該模組時就試圖**程式將如何變化。相反,你是以最簡單的方式編寫的。直到需求最終確實變化時,你才修改模組的設計,使之對該種變化保持彈性。
注:你的程式遵守了物件導向程式設計中的開放-封閉原則(ocp)和依賴倒置原則(dip)。[見以下章節]
設計的腐化是一種症狀,是可以主觀(如果不能客觀的話)進行量度的。腐化常常是由於違法了設計原則中的乙個或多個所導致的。例如,僵化性常常是由於對開放-封閉原則(ocp)不夠關注的結果。
開發團隊應該運用相應的設計原則來去除腐化。但當軟體還沒出現腐化時不應該應用這些原則。僅僅因為是乙個原則就無條件的去遵循它的做法是錯誤的。這些原 則不是可以隨意在系統中到處噴灑的香水。過分遵循這些原則會導致不必要的複雜性(needless complexity)的設計臭味,變成另一種腐化。
下一章:物件導向軟體設計原則(三) —— 軟體實體的設計原則
codeproject
軟體設計原則
開閉原則 ocp 軟體設計的最大原則 這個原則說的是 對擴充套件開放,對修改關閉。其實意思是說,給系統新增新的功能,但不修改原有 如果能做到呢,關鍵在於抽象化,也就是封裝變化,抽象層不變,讓具體實現依賴抽象隨需求變化。使得系統具有很強的擴充套件性和可維護性。黎克特制代換原則 任何基類可以出現的地方,...
軟體設計原則
高內聚 低耦合 乙個軟體系統要有乙個穩定的架構,不會隨需求的改變而發生巨大的變動。因此,高內聚 低耦合是乙個軟體系統設計中必須遵循的基本原則 面向抽象程式設計 在面向過程的軟體開發中,上層元件呼叫下層元件,就意味著上層元件依賴於下層元件,當下層元件發生劇烈變化時,上層元件也要跟著一起發生變動,這將導...
軟體設計原則
軟體開發中有以下一些基本原則,深刻掌握這些原則比掌握一門技術要重要。1.開閉原則 open closed principle,ocp 乙個軟體應當對擴充套件開放,對修改關閉。也就是說我們在設計軟體時,應當可以在不必修改源 的情況下改變 擴充套件 其行為。開閉原則是非常重要的設計原則,其它的設計原則實...