一個完整的面向對象分析與設計例子

首先說明,接下來   這部分內容,跟面向對象沒什麼關係,只是描述出我們接下來 "需要做什麼 ". 

大家都知道電梯是怎麼回事了,所以獲取需求的過程我就不囉嗦了,直接把最後結果描述出來.(對於計算機專業學生或軟件工程畢業設計的需求分析結果應該有些參考意義...起碼可以看出怎麼樣的結果才真正有意義) 

電梯樓層   1-10   樓(也就是沒有什麼地下室也沒有中間跳過某些樓層,最普通的情況),一共有2部電梯.   
如果一個在n樓(1 <10)的乘客按了下行按鈕,那麼下一個正在向下走的電梯到了n樓必須停下接收乘客.   
如果電梯裏已經沒有乘客了,電梯應該留在原位置直到再次投入使用.   
在將乘客送到目的地以前電梯不允許反向運動.(也就是電梯比如把乘客從9樓帶到樓下,如果在走到4樓的時候5樓有人要下,電梯不能從4樓回5樓去,而要把乘客帶到樓下再回來)   
滿載的電梯不再收人.   
電梯裏有個按鈕面版,裏面有10個樓層的按鈕.   按下按鈕表示該樓層變成一個目的地,按鈕會發光,到達以後按鈕不再發光.   
建築物2-9樓層有一個按鈕面版,上面兩個按鈕分別是向上和向下.1樓只有向上,10樓只有向下.   
電梯有一個控制中心來自動控制,不用人手干預.   
...............(其他,略) 
第二部分     到底上面的需求描述中,哪些東西可以成爲我需要來   "面向 "的 "對象 "? 

首先,我們要選擇出候選的對象,然後再在候選對象裏選擇真正爲程序設計所使用的對象. 

候選對象的選擇有很多方式,比方說 "名詞短語頻率分析 ",對上面那段去分析看哪些名詞出現次數很多,說明可能比較重要,可以直接拿來當候選對象.   比如樓層,電梯,按鈕,乘客,都出現很多次.   當然還有另外的方式,比如 "按方面建立 "   (PS:   I 'm   sorry,我是個實用主義者,只要目的達到了...我不喜歡像一些書本上那樣用些概念糊弄人,上面紅字的兩個方法的名稱是我臨時取的.....也許不好聽,也許他們有更優美的名字.....反正這不是我們該擔心的,留給教材編寫人員取操心吧...)   從   人\組織\設備\物品\業務\事件\表格     幾個方面去找對象去.   

  這裏要注意的是對象的選擇要看具體情況的,比如   ....   我們把   "放飛希望 "   作爲一個具體的對象實例   (   ^_^   ),   如果在醫療系統中,可以抽象成   "人 "   這個類,由   腦袋\身體\手\腳......等部分.       如果是在教育管理系統中,   就不能這麼處理,可能要抽象成   "老師 "   這個類,   包括   教學年限\工資\所教科目\.....   等內容. 

還有一點需要注意的是不要跨過系統邊界.   系統之外的事情就不要管了,就說我們這個電梯調度,   電梯的生產日期啊什麼的,還有乘客的姓名,   都根本雨系統無關,這些是不需要的. 

最後有一點被非常非常非常非常非常多的工程師們所忽視的就是建立面向對象分析模型的時候,   只應該考慮邏輯,而不要去考慮跟實現技術相關的東西.   比如按鈕是塑料的還是金屬的,   (當然這個很明顯是分外的事,大多數人在做調度分析的時候不會考慮這個,不過有些情況卻很隱蔽..)   

現在回到我們具體問題,假設現在我們列出來的初步的清單是這樣的(可能100個人有100個列法,不過沒關係,這是正常的...):   電梯\電梯門\電梯位置\乘客\目的地按鈕\大樓..... 

這離真正可以用的對象還差很多,   我們需要對每個詞進行分析,看看到底是不是,值得不值得把這個當一個對象來考慮.我們分析從以下幾個方面看: 

它在系統裏有功能嗎?   (比如電梯天花板   就該刪除掉....如果有的話....)   
其他對象需要這個對象幫忙做點什麼事嗎?   (比如乘客需要電梯把他帶上去...)   
其他對象需要這個對象的數據嗎?(比如控制中心明顯需要知道電梯現在在哪..)   
這個對象是不是包含了有用卻不對別的對象公開的內容.(比如電梯上面的吊鏈和發動機明顯是有用的,不然電梯無法動也就無法調度了,   但是明顯對於其他對象,都是不需要公開的,   不管是乘客還是控制中心都不需要控制電梯的馬達---這裏可能有人要有意見了,   控制中心不控制電梯馬達麼?   答案是不該控制,控制中心應該只告訴電梯你要向上還是向下還是停,   至於馬達轉多快怎麼轉,是電梯自己的電路來控制的纔對,   責任分開明確有利於系統的維護.)   
這個對象有沒有一個生存期,是不是描述了     產生--各種運行狀態--消亡   的信息.比如一次電梯召喚-(誰想個好聽點的名字?   呵呵....)   產生是用戶按了向上或向下的按鈕.   然後進入   "電梯來 "   這個狀態,   再進入電梯停(包括開門,乘客進去,關門)的狀態,然後又是   "電梯走 "   的狀態,再之後是電梯再停給用戶下去,然後這個電梯召喚以電梯停止移動等待下次召喚作爲結束.   
如果嚮應用領域的專家(這裏比如是電梯工程師)描述這個對象,他是否可以馬上明白重要性?   (比如如果你對一個電梯工程師說   "調度工廠 ",他應該就不明白..因爲工廠模式是面向對象設計的手段,跟電梯這個主題本身沒多少聯繫) 
以上列出的6點,不一定要每點都符合,不過如果每點都不符合,那不用考慮...刪掉吧........ 

然後考慮系統以外的情況了.     這些對象中: 

系統能識別嗎?   (比如對於超重還是不超重...有些就可以識別,有些電梯就不能..)   
哪些是系統必須做出響應的(用戶按的按鈕明顯就是一個...)   
對於發生的事件(比如按下按鈕,比如電梯門打開)   哪些對象可以識別它?   
有沒有沒用上的?   
還需要其他對象嗎?   
有沒有不能識別又不需要響應的對象(比如用戶的帽子????     ^_^)   ,不過要注意,並不是絕對的,在極少數情況下,不能識別又不需要響應的對象是有用的.不過那是後話了,先不考慮它....   
系統需要跟其他系統相連麼?   (比如,跟火災警報系統相連,   火災發生時不準打開電梯門...這裏當然我們不考慮這麼複雜,但是畢竟是存在這樣的東西)   
用於溝通兩個對象的對象是不是不小心漏掉了?   (這是常見的) 
在你刪除一個候選對象的時候,問問:   去掉以後系統會怎樣?   如果我要重用一個包含了這個對象的一大塊功能,這個對象到底會給真的用上麼?(注意重用是非常重要的,絕對不要爲了一個地方而寫程序,程序要可以在不同地方重用,就像不會有生產一個電視機的電視機廠一樣) 

不斷重複上面的過程,最終,你應該從不斷增加新東西和不斷刪除東西后獲得一個有些用的列表,假設如下面這樣: 

到達事件:   (就是表示電梯到了,包括開門,進電梯,關門..還有燈亮燈滅等一系列內容..)   
到達按鈕面版(就是電梯裏那10個按鈕)   
電梯(這個沒什麼疑問了........)   
樓層   
...............................(其他略) 
然後要做的事情是給每個詞一個文字描述,比如   電梯的描述   如下 

Elevator   (類名一般請不要用中文.....雖然你要用中文也行....):執行電梯的控制(上走,下走)和報告功能(告訴控制中心現在自己在幾樓..),   內部封裝(就是不對外公開它是怎麼做的,只公開結果)了   控制電梯運行(馬達怎麼轉..),   報告電梯位置,識別電梯是否準備就緒的各種服務.   他包括的屬性是電梯的   運動方向\位置\狀態   方面的信息. 

最後出來的對象不可能證明就是對的,也一般不會全錯,   ^_^     人的問題.....注意最終用戶也許會對這個有影響,所以最好還要問問他們,如果他們提出些比較麻煩的意見,如某種特定實現技術(比如如果電梯這個樓是一家生產音響的,也許老總有想法就要在電梯裏有個音箱,電梯每到一層樓會報一次位置........-_-   .....)   應該用專業角度勸他不要這樣,如果他一定要.........那你也沒辦法了,誰讓他是出錢的...   ^_^ 

這個模型可能會在後面被修改很多,做好心裏準備.   

最後說一下命名,   對象的類名   應該是個類而不是個功能.或一個屬性.   比如   可以叫 "電梯 "   卻不好叫   "電梯顏色 "或   "電梯上升 ".   對象類名最好全部都惟一,實在不好惟一了,就用不同的包分開,就像把相同文件名的不同文件放不同的文件夾裏就行.   對象名稱必須在應用領域有意義(也就是在這裏必須在電梯界有意義,   而不能只在軟件界有意義..)   用   名詞或   形容詞+名詞,   不要用   名詞+動詞   的方式.   不要出現   "和 ", "或 ", "與 "之類的內容.(比如人就叫   Humen   不要叫   LadiesAndGentalmen....   ^_^   ) 

現在,大家列出些什麼對象了呢?   

ArrivalEvent   
ArrivalPanel   
DestinationEvent   
DestinationPanel   
Elevator   
ElevatorMotor   
Floor   
OverweightSensor   
SummonsEvent   
SummonsPanel   
....................................................... 
大概是這樣的一些東西,(可能跟我這個差十萬八千里,不過沒關係,那也是正常的.....如果照上面說的做了,那應該也錯不到哪去..) 

好了,.....今天先到這,未完待續 
拋磚引玉,   思考面向對象是什麼?   (面向對象系列之二) 

 

 
上一個文章駁斥了別人誤人子弟,不過深刻記得十幾年前老師就教過,   如果你自己不能做得更好,就不要說別人不好. 
所以這篇聊聊跟面向對象有關係的話題,希望拋磚引玉. 

先考慮一個現實問題,   大家都熟悉的手機發短信.   來看看早期(A   大約是彙編語言時代),中期(B   結構化),現在(C   面向對象)三種思想下的不同實現.   
我說的是思想,   因爲雖然現在大家使用着面向對象的工具,但是大部分程序員的思想依然沒有面向對象.   比如現在我手下這羣程序員裏有面向對象分析和設計能力的也就一個.. 
用最面向對象java和C#也可以寫出雜亂無章完全不面向對象甚至不結構化的程序. 

注意到現在我們的手機號碼分成移動和聯通兩種,   雖然對我們來說,不過是號碼不一樣收費不太一樣,沒多少區別,但是兩家的短信接口可是完全不一樣的. 
假設程序要求     用戶在界面上輸入手機號碼(TextBox1),輸入一條短信內容(TextBox2),按確定(Button1),就可以把短信發到那個手機上 

A   一步一步走,該幹什麼就幹什麼...看看僞代碼: 

st***號碼   =   TextBox1.Text; 
st***內容   =   TextBox2.Text; 
int   第3位數字   =   int.Parse(號碼.Substring(2,1));   //把第3位取出來,用來判斷是不是移動的手機     如   1390000000   就取出一個   9     
if(第3位數字   >   3) 

.... 
.... 
....//這裏是一堆長長的代碼用來發送***的短信...省略,我們這裏只說程序的思想..不涉及技術細節 
}   
else 

.... 
... 
...//又是一堆長長的代碼用來發送***的短信 


  

B   寫一個庫,定義出發送***短信的函數和發送***短信的函數,還有判斷的函數,假設函數原型分別是     
發送移動短信(st***手機號碼,st***內容); 
發送聯通短信(st***手機號碼,st***內容); 
bool   是否是移動號碼(st***手機號碼); 

然後寫程序如下: 

if(是否是移動號碼(TextBox1.Text)) 
        發送移動短信(TextBox1.Text,TextBox2.Text); 
else 
        發送聯通短信(TextBox1.Text,TextBox2.Text); 

  


C   定義一個抽象接口   "短信接收者 ",     由   "*** "和   "*** "   兩個類分別實現接口.   各自實現發送短信方法.   
然後構造一個   "手機工廠 "(一時想不到好的名字,暫時叫這個吧)   ,   接收一個號碼,返回一個   "短信接收者 "接口(裏面根據接收的參數,可能是***或***) 

然後程序如下(一行..): 

手機工廠.獲取接受者(TextBox1.Text).發送(TextBox2.Text); 

或寫成這樣清晰點:   

st***號碼   =   TextBox1.Text; 
st***內容   =   TextBox2.Text; 
手機工廠.獲取接受者(號碼).發送(內容); 

  


OK,對於上面3段僞代碼     大家有什麼想法?   第3種是不是看起來有點   爽?   也許把,也僅僅是看起來那麼一點爽,沒什麼大不了.   
沒錯,面向對象是在大型的地方更能體現優勢,一小堆是展現不出來的.   我們假設程序中一共有100個這樣的地方(比如一個是發短信的,一個接短信的,一個打電話的,一個上網的.....) 

那麼對於A程序,很抱歉,非常要命,要在100個地方複製代碼,複製100份,然後對其中99份做修改(或多或少,總要改點..) 
B程序只是在每個調用的地方加幾行,可以接受. 
C程序在調用點也是加1行,同樣也可以接受. 

這個時候,結構化和麪向對象共同的優點體現出來了,複用性   (教科書中講面向對象總是說說複用是面向對象比其他方法的優勢,其實結構化本身就是可複用的) 
A方法差不多該拋棄了........這就是結構化發展起來以後,   非結構化很快面臨淘汰地步的原因,因爲在軟件稍微大點,就出麻煩,寫寫單片機小模塊還行. 

軟件在一天天變大變複雜,僅僅是變大變複雜而已?   當然不是.   也變得多變.   用戶的需求時時在變.軟件也容易變,. 
回到剛纔的問題,   現在不是有小靈通麼?   你又需要多一種類型,變成     小靈通\移動\聯通     3種類型. 
那麼對於   A   ,災難發生....修改程序的成本不比重新做一個少. 
對於B   需要去100個調用的地方多加一個if來判斷,然後多加一個對應小靈通的函數.   修改量有點大,不過也不是不行,因爲畢竟現在的工具發達,你可以查找--替換. 
不過程序是需要測試的,你替換一個地方,就需要多測試一個地方,成本高. 
對於C   多加一個實現   接口的     "小靈通 "   類   ,   然後修改   "手機工廠 "的   "獲取接受者(st***號碼) ".   一共2處,測試也只要再測試   這個新類   還有一個方法. 


C   方法   面向對象的優勢在這個時候體現出來了. 


有人這個時候出來抗議了,如果程序寫的多了,經驗豐富了,有人會看出我上面那些假設的漏洞,就是B   並不是最好的結構化方法,   因爲   其實有更好的用一個函數來實現判斷類型 
那樣就跟   C   一樣,只要改很少的地方了.   

沒錯,   那樣C和B又公平平等了,C還是沒什麼優勢.     請注意2點     第一:   "面向對象 "   不是指   面向對象   的   編程語法,   而是一種思想.   那樣寫其實   B   已經拿到了一點面向對象的思想了 
只是封裝在非面向對象的語法中.     第二   不面向對象的確可以寫出低耦合的,高效的,可維護的,很牛逼的程序.     但是那是需要很高造詣的人來做的事.   因爲沒有類的封裝性,名字空間的隔絕 
還有全局性的變量在程序裏走,   要靠程序員自己去避免這些 "可以做,可以方便地做 "卻 "會對未來維護帶來災難 "的操作,   對程序員要求很高,你要自覺不用全局變量,就像以前自覺不 
用goto語句....還要自覺把功能分好擺好,   需要的分析設計技術是很高的.     而寫出同樣質量的面向對象程序,只要略知道設計模式的人就都可以了.   這就是面向對象大行的原因之一. 

有人說,面向對象就真的封裝了?可重用了?   可是我看見很多C#和java程序錯亂複雜,   根本拿不出一個   "塊 "   出來用,你拿了   "塊A "   就調用到   "塊B ",   非要把 "塊B "也拿來..然後又要 
用到無關的C,D,E,F.....最後出來一大落,而且99.9999%是我不需要的,我就只需要那0.00001%而已....   

這是現實,的確,至少我看見的代碼裏垃圾代碼佔多數(這裏是指可以實現功能卻很 "有臭味 "的代碼),   這主要有一個很大的原因是寫代碼的人沒有面向對象的思想,有的只是面向對象 
工具包裝的面向過程思想,而且連結構化都說不上.     不是面向對象的錯. 

  

差不多,有些人現在認同面向對象了,也知道這不是書上隨便說的那些苦澀的概念了,不過還是不明白怎麼個面向對象法.   我再換個話題說說,不說手機吧, 
說衣服,服裝廠生產衣服.   衣服有顏色,有大小,有款式....   看看一個設計,在不同的人手裏是什麼不同的方法. 
現在服裝廠要   生產一批   藍色的,   小號的,   女款的...的衣服... 

A   :     
衣服       衣服1   =   new   衣服(); 
衣服1.顏色   =   蘭; 
衣服1.號碼   =   小; 
衣服1.款式   =   女式; 
..... 

然後new   出好多件來.賦值好多下.... 


現在問題是突然說不要蘭的了要紅的,   哎喲....改啊改....   當然你可以在循環裏做這個,但是如果每件衣服除了顏色和款式一樣,  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章