關於面向對象與面向過程(面向結構)的區分

面向對象和結構化方法的比較

 

最近又有客戶問起結構化方法(即結構化分析SA Structured Analysis 和結構化設計SD Structured Design)和麪向對象分析設計(OOAD Object-Oriented Analysis & Design)方法的區別,這是一個很多人談了很多遍的問題,Google一下就可以找到很多以此爲標題的文章。OO技術發展了很多年了,現在大家都在用,已經沒有什麼異議了,幾乎沒有人會懷疑這種技術的好處。但它明顯是一位過氣的明星,就連前幾年纔出道的模型驅動開發(MDD)也已經過了風頭最勁的時段,我們現在更熱衷於討論一些更加時尚的概念如:SOA、IT治理、循規等。

   要想了解結構化方法和麪向對象方法,去看一下 Roger S.Pressman 寫的"軟件工程:實踐者的研究方法(Software Engineering: A Practitioner's Approach)"(這是一本很流行的書,網上就有賣)就可以了,裏面有專門的章節介紹這兩種方法及其區別。

    我喜歡把軟件系統描述成對現實世界的映射,現實世界中的我去ATM機取了100元錢,映射到軟件就是從我的銀行帳戶對應的數據庫記錄中餘額(Balance)那一字段減去100。所謂的結構化方法和麪向對象方法,就是兩種不同的映射手段,結構化方法是以處理過程爲中心,強調先定義數據結構(ER實體關係建模),然後分析處理邏輯(DFD數據流圖);面向對象方法則主張兩者之間的自然映射,在ATM取款的例子中,我被映射爲Customer對象,我的帳戶被映射爲Account對象(在對象-關係映射中再對應到數據庫表Account中的一條數據記錄)。正因爲OO方法採用對現實世界的自然映射,現實世界中的業務流程發生變化時,軟件實現也可以比較方便地跟着轉變;而採用結構化方法的映射(或是採用OO方法,但對業務流程的映射關係建立不當),軟件上相應修改的工作量就會大一些。

    應當說明的是這兩種開發方法之間並不是一個完全對立的關係,結構化方法出現在前,並且得到了很成功的應用;面向對象方法誕生在後,說它是從結構化方法發展而來也不爲過,它也繼承了很多結構化方法中的成功經驗如:數據抽象、自頂向下、模塊化、高內聚、低耦合等,我們應該把OO技術看作是軟件設計方法的最佳實踐經驗整合。結構化方法在過去也很成功,但是我們所開發的軟件規模越來越寵大,軟件系統越來越複雜,20年以前開發一個軟件可能是100%從頭開發的,現在開發一個軟件可能只有10~20%的代碼是新開發的,很多功能尤其是基礎功能都是可以重用的。J2EE技術就是一個典型的例子,它把很多基礎功能如會話(Session)管理、事務(Transaction)管理、對象關係映射(O-R Mapping)等都已經在中間件中實現了,你只需要重用它們就可以了。要提高軟件重用性的話,就一定要引入OO方法中的一些關鍵理念如:封裝、多態、抽象層次結構等,這些機制可以提高軟件的可重用度,幫助我們有效地管理軟件系統日益增長的複雜度。

    實際上結構化方法和麪向對象方法之間並不是革命性的變化,我把它們比喻爲“走路”和“跑步”的區別,原始人爲了捕捉獵物,必須跑步前進;跑步跟走路的區別不是很大,僅僅是邁步頻率加快,並且雙腳可能同時離地而已。而模型驅動開發(MDD)則是一種革命性的變化,我把它比喻成“騎自行車”,因爲MDD已經開始利用自動化工具來提高軟件開發生產率了,但是MDD技術還剛剛處於探索階段,還沒有成爲一項大規模應用的成熟技術。對應於這種比喻的話,我們可以看到軟件開發技術發展是非常緩慢的,相比其他行業(如集成電路)的技術發展,軟件開發技術還很落後。我們大部分人還在使用原始的開發手段,等到將來我們把軟件開發技術發展到“開汽車”、“坐飛機”的階段,可能我們就找到了軟件工程的“銀彈”。那個時候可能我們只需要把需求告訴電腦就行了,它就會自動地做到我們想要的東西。
                走路     跑步      騎自行車    開汽車、坐飛機
                結構化方法  面向對象方法  模型驅動開發  將來的軟件開發技術 

    我觀察到在實際應用中使用什麼樣的設計方法往往受制於編程技術和環境,採用COBOL(主機應用開發)、C等傳統過程語言的開發人員一般都在使用結構化方法進行設計;而採用Java、C++等面嚮對象語言的開發人員受到對象概念的薰陶比較多,基本上會轉向面向對象方法;當然也有例外,也有一些開發人員在用C++語言實現面向過程的設計,因爲他們還沒有領悟OO所帶來的好處。如果你想從結構化方法轉向OOAD方法的話,我建議你先從OOP(Object-Oriented Programming)開始,使用Java、C++這些經典的OO編程語言可以讓你逐步掌握OO技術的妙處,這樣再轉向OO設計就會容易一些,這是自底向上的實踐。

********************************************************************************

 

 

 

 

面向對象和麪向過程區別的很好地範例

 面向過程就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。 

面向對象是把構成問題事務分解成各個對象,建立對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。

例如五子棋,面向過程的設計思路就是首先分析問題的步驟:1、開始遊戲,2、黑子先走,3、繪製畫面,4、判斷輸贏,5、輪到白子,6、繪製畫面,7、判斷輸贏,8、返回步驟2,9、輸出最後結果。把上面每個步驟用分別的函數來實現,問題就解決了。

而面向對象的設計則是從另外的思路來解決問題。整個五子棋可以分爲 1、黑白雙方,這兩方的行爲是一模一樣的,2、棋盤系統,負責繪製畫面,3、規則系統,負責判定諸如犯規、輸贏等。第一類對象(玩家對象)負責接受用戶輸入,並告知第二類對象(棋盤對象)棋子佈局的變化,棋盤對象接收到了棋子的i變化就要負責在屏幕上面顯示出這種變化,同時利用第三類對象(規則系統)來對棋局進行判定。

可以明顯地看出,面向對象是以功能來劃分問題,而不是步驟。同樣是繪製棋局,這樣的行爲在面向過程的設計中分散在了總多步驟中,很可能出現不同的繪製版本,因爲通常設計人員會考慮到實際情況進行各種各樣的簡化。而面向對象的設計中,繪圖只可能在棋盤對象中出現,從而保證了繪圖的統一。

功能上的統一保證了面向對象設計的可擴展性。比如我要加入悔棋的功能,如果要改動面向過程的設計,那麼從輸入到判斷到顯示這一連串的步驟都要改動,甚至步驟之間的循序都要進行大規模調整。如果是面向對象的話,只用改動棋盤對象就行了,棋盤系統保存了黑白雙方的棋譜,簡單回溯就可以了,而顯示和規則判斷則不用顧及,同時整個對對象功能的調用順序都沒有變化,改動只是局部的。

再比如我要把這個五子棋遊戲改爲圍棋遊戲,如果你是面向過程設計,那麼五子棋的規則就分佈在了你的程序的每一個角落,要改動還不如重寫。但是如果你當初就是面向對象的設計,那麼你只用改動規則對象就可以了,五子棋和圍棋的區別不就是規則嗎?(當然棋盤大小好像也不一樣,但是你會覺得這是一個難題嗎?直接在棋盤對象中進行一番小改動就可以了。)而下棋的大致步驟從面向對象的角度來看沒有任何變化。

當然,要達到改動只是局部的需要設計的人有足夠的經驗,使用對象不能保證你的程序就是面向對象,初學者或者很蹩腳的程序員很可能以面向對象之虛而行面向過程之實,這樣設計出來的所謂面向對象的程序很難有良好的可移植性和可擴展性。

 

 

*********************************************************************************************

面向對象跟結構化的區別與分析

按我的理解,所謂的面向對象就是就是在程序設計中按類來對系統進行設計。舉個例子,要發廣告郵件,廣告郵件列表存在數據庫裏面。倘若用C來寫的話,一般會這樣思考,先把郵件內容讀入,然後連接數據庫,循環取郵件地址,調用本機的qmail的sendmail命令發送。
  然後考慮用Java來實現,既然是OOP,就不能什麼代碼都塞到main過程裏面,於是就設計了三個類:
    一個類是負責讀取數據庫,取郵件地址,調用qmail的sendmail命令發送;

  一個類是讀郵件內容,MIME編碼成HTML格式的,再加上郵件頭;

  一個主類負責從命令讀參數,處理命令行參數,調用發email的類。

仔細的分析一下,就會發現這樣的設計完全是從程序員實現程序功能的角度來設計的,或者說,設計類的時候,是自低向上的,從機器的角度到現實世界的角度來分析問題的。因此在設計的時候,就已經把程序編程實現的細節都考慮進去了,企圖從底層實現程序這樣的出發點來達到滿足現實世界的軟件需求的目標。類與類之間是通過發送和接收消息相聯繫的,接收消息的對象通過調用類的方法來實現相應的操作。訪問限制符Private、protected和public將類分成三個部分:私有部分、保護部分和公有部分。使數據具有不同的隱蔽程度。類定義可包含一組構造函數和析構函數,構造函數保證了在聲明類的對象時對其自動初始化,而析構函數則保證對類的對象正常地清除。從已有的類還可以派生瓣的類,前者稱爲基類,後者稱爲派生類,OO方法中繼承的原則在這裏得以體現。能夠突破類的私有部分,禁止其它函數直接訪問限制的友員機制,以及由運算符重載、函數名重載和虛函數構成的多形性,使程序員能以更自然、方便的表達方式實現對象的操作。

    當然萬事萬物皆爲對象,而對象又是類的實例,因此在面向對象的編程中,就如上面的例子,我們可以在分析三個大類的前提下,定義這些類的對象。大家都知道任何對象都會有

屬性,方法,過程,函數,事件等,且類與類之間又可以繼承,子類繼承父類,子類間有多態性。類也可以先進行封裝,再使用。這些都是面向的特性,當然它還有一個代碼可重複使用的特性。也是因爲如此,在程序設計中如果採用面向對象的編程方式可以更好的以人類的正常思維進行分析。至於具體的分析,到後面將用俄羅斯方塊來分析。相比之下,傳統的結構化程序設計則採用另外一種編程方式,它是採用按模塊功能進行系統分析的,下面附出結構化方法的大概流程圖。圖略
 

結構化程序設計的主要思想是功能分解並逐步求精,當一些任務十分複雜以至無法描述時,可以將它拆分爲一系列較小的功能部件,直到這些自完備的子任務小到易於理解的程度。例如,計算一個公司中每一個職員的平均工資是一項較爲複雜的任務。可以將其拆分爲以下的子任務

(1)       找出一個人的收入

(2)       計算總共有多少職員

(3)       計算工資總額

(4)       用職員人數去除工資總額

而計算工資總額本身又可以分爲一系列子任務:

(1)       找出每個職員的檔案

(2)       讀出工資數額

(3)       把工資加到部分和上

(4)       讀出下個職員的檔案

類似地,讀出每個職員檔案的記錄又可以分解爲一系列子任務

(1)       打開職員的檔案

(2)       找出正確記錄

(3)       從磁盤讀取數據

具體看來結構化程序具有以下幾個特徵:自頂向下,逐步細化,模塊化設計,結構化編碼

從軟件工程發展的歷史來看,早期的軟件開發量小,結構化程序設計在成功地爲處理複雜問題提供了有力的手段。然而到80年代末,它的一些缺點越來越大。比如當數據量增大時,數據與處理這些數據的方法之間的分離使程序變得越來越難以理解。對數據處理能力的需求越強,這種分離所造成的負面影響越顯著。並且對於每一種老問題的新方法都要帶來額外的開銷,與可重用性相比,這種編程思想顯得落後許多。面向對象就是在這種情況產生了,相信在未來十年內,面向對象還將繼續發展。

    介紹完了兩者大致的定義後,接下來用俄羅斯方塊來分析一下兩種編程思想具體的不同

對於結構化程序的方法,在這裏用腳本語言javascript來描述,而面向對象則採用如日中天的java來分析。

首先按結構化編程方式來完成俄羅斯方塊的話,我們會對問題按數據結構進行模塊化的分解,本程序將用以下幾個模塊來完成:

(1)       開始遊戲(function beginGame())

(2)       俄羅斯方向鍵的控制(function keyControl())

(3)       塊的消除(function decline())

(4)       面板的移動(function moveBar())

(5)       暫停遊戲(function pauseGame())

(6)       遊戲重新開始(function replayGame())

(7)       方塊的顯示(function randBar())

在分解這幾個模塊後,就已經大致完成了對程序的整體分析了。這是對程序中問題的描述,接下來則是具體的算法分析,也是結構化當中的解決問題了。要解決本程序中最主要的一個算法一一方塊的顯示算法,在本程序中採用四維數組的方式來表達方塊,比如:

1,0,0,0

1,0,0,0

1,0,0,0

1,1,1,1

這樣則表示方塊中的L形狀態,其他則不在一一列出。其他算法的則略過。還是把重點轉移到面向對象中來。

在面向對象中首要解決的是本程序中要分爲幾大類來完成最基本的系統設計。這個俄羅斯方塊放在網頁中,也就是採用applet小程序的形式,但由於java跨平臺性,它可以在windows,linux,unix等操作系統中使用。本程序大致分爲三個大類:Tetrisapplet,Brick3D,Board。下面是TerisApplet的UML圖:                                 
 

                                      TerisApplet

                                  Init()

                                  Initcomponent()

                                  Minewgameactionperformed()

                                  Miendgameactionperformed()

 



(1)       init():初始化applet

(2)       initcomponent():初始化窗口以及窗口中的組件

(3)       minewGameActionPerformed():當選擇菜單或者工具欄上的“開始遊戲”命令後,開始新遊戲。

(4)       miEndGameActionPerformed():當選擇或者工具欄上上的“結束遊戲”命令後,結束遊戲。

2.Brick3D
Brick3D定義主要有reset(),getShapeIno(),getAngle(),getShape(),rotate()和paint()方法。其中getshapeInfo(),getAngle(),getShape()用於獲取磚塊的形狀信息,矩陣、角度和形狀值。Totate()方法用於旋轉磚塊,paint()方法用於在指定的地點給制磚塊
  
 

3.Board

Board類實現俄羅斯方塊的主要大部分功能,下面是它的UML圖

 略


 

從建模圖中可以看出:Board實現的方法有drop()、move()、reset()、newGame()、endGame()、run()、paint()、createNewBrick()、checkRow()、processKeyEvent()、processMouseEvent()、processMouseWheelEvent()等方法,這些方法的功能定義如下。

(1)       drop():磚塊的下落函數。

(2)       move():移動磚塊,1表示向右移動,-1表示向左移動

(3)       reset():初始化board_info數組

(4)       newGame():開始執行線程(遊戲)

(5)       endgame():停止執行線程(遊戲)

(6)       run():線程運行的函數

(7)       paint():繪製背景,邊界和磚塊

(8)       createNewBrick():創建下一個新的磚塊形狀、角度和顏色,角度、形狀和顏色隨機產生

(9)       checkrow():檢查是否可以消行,並計算分數

(10)   processKeyEvent():處理鍵盤事件,控制磚塊的移動和旋轉

(11)   processMouseEvent():處理鼠標事件,控制旋轉和左右移動

(12)   processMouseWheelEvent():處理鼠標滾輪事件,當向下滾動時,控制塊向下移動

分析完後,本程序用了三個主要的類:Brick3D對象、Board對象和TerisApplet對象,分別對應Brick3D.java,Board.java和TetrisApplet.java三個文件,在實例中還包括了一個Html文件。

  雖然用面向對象寫的代碼更多,看似面向對象更爲麻煩,但實際上面向對象更能解決現在的軟件工程問題,而結構化程序設計的方法只對小型的軟件設計適合。未來十年內面向對象還是軟件開發的主流。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章