| 導購 | 订阅 | 在线投稿
分享
 
 
 

過程式設計和面向對象設計的比較(組圖)

來源:互聯網  2008-05-31 11:18:01  評論

本文比較了過程式設計和面向對象設計,主要論述了過程式設計在程序開發中的一些問題及面向對象設計是如何解決這些問題的。

過程式設計

總的來說,過程式的程序設計是一種自上而下的設計方法,設計者用一個main函數概括出整個應用程序需要做的事,而main函數由對一系列子函數的調用組成。對于main中的每一個子函數,都又可以再被精煉成更小的函數。重複這個過程,就可以完成一個過程式的設計。其特征是以函數爲中心,用函數來作爲劃分程序的基本單位,數據在過程式設計中往往處于從屬的位置。

過程式設計的優點是易于理解和把握,這種逐步細化問題的設計方法和大多數人的思維方式比較接近。

然而,過程式設計對于比較複雜的問題,或是在開發中需求變化比較多的時候,往往顯得力不從心。這是因爲過程式的設計是自上而下的,這要求設計者在一開始就要對需要解決的問題有一定的了解。在問題比較複雜的時候,要做到這一點會比較困難,而當開發中需求變化的時候,以前對問題的理解也許會變得不再適用。事實上,開發一個系統的過程往往也是一個對系統不斷了解和學習的過程,而過程式的設計方法忽略了這一點。

在過程式設計的語言中,一般都既有定義數據的元素,如C語言中的結構,也有定義操作的元素,如C語言中的函數。這樣做的結果是數據和操作被分離開,輕易導致對一種數據的操作分布在整個程序的各個角落,而一個操作也可能會用到很多種數據,在這種情況下,對數據和操作的任何一部分進行修改都會變得很困難。

在過程式設計中,main()函數處于一個非常重要的地位。設計者正是在main()函數中,對整個系統進行一個概括的描述,再以此爲起點,逐步細化出整個應用程序。然而,這樣做的一個後果,是輕易將一些較外延和易變化的邏輯(比如用戶交互)同程序的核心邏輯混淆在一起。假設我們編寫一個圖形界面的計算器程序和一個命令行界面的計算器程序,可以想象這兩個版本的main()函數會有很大的差異,由此衍生出來的程序很有可能也會迥然不同,而這兩個版本的程序本應該有很大部分可以共用才對。

過程式設計還有一個問題就是其程序架構的依靠關系問題。一個典型的過程式程序往往如Figure 1所示:

過程式設計和面向對象設計的比較(組圖)

Figure 1

圖中的箭頭代表了函數間的調用關系,也是函數間的依靠關系。如圖所示,main()函數依靠于其子函數,這些子函數又依靠于更小的子函數,而在程序中,越小的函數處理的往往是細節實現,這些具體的實現,又經常變化。這樣的結果,就是程序的核心邏輯依靠于外延的細節,程序中本來應該是比較穩定的核心邏輯,也因爲依靠于易變化的部分,而變得不穩定起來,一個細節上的小小改動,也有可能在依靠關系上引發一系列變動。可以說這種依靠關系也是過程式設計不能很好處理變化的原因之一,而一個合理的依靠關系,應該是倒過來,由細節實現依靠于核心邏輯才對。

面向對象設計

面向對象是一種自下而上的程序設計方法。不像過程式設計那樣一開始就要用main概括出整個程序,面向對象設計往往從問題的一部分著手,一點一點地構建出整個程序。面向對象設計以數據爲中心,類作爲表現數據的工具,是劃分程序的基本單位。而函數在面向對象設計中成爲了類的接口。

面向對象設計自下而上的特性,答應開發者從問題的局部開始,在開發過程中逐步加深對系統的理解。這些新的理解以及開發中碰到的需求變化,都會再作用到系統開發本身,形成一種螺旋式的開發方式。(在這種開發方式中,對于已有的代碼,常需要運用Refactoring技術來做代碼重構以體現系統的變化。)

和函數相比,數據應該是程序中更穩定的部分,比如,一個網上購物程序,無論怎麽變化,大概都會處理貨物、客戶這些數據對象。不過在這裏,只有從抽象的角度來看,數據才是穩定的,假如考慮這些數據對象的具體實現,它們甚至比函數還要不穩定,因爲在一個數據對象中增減字段在程序開發中是常事。因此,在以數據爲中心構建程序的同時,我們需要一種手段來抽象地描述數據,這種手段就是使用函數。在面向對象設計中,類封裝了數據,而類的成員函數作爲其對外的接口,抽象地描述了類。用類將數據和操作這些數據的函數放在一起,這可以說就是面向對象設計方法的本質。

在面向對象設計中類之間的關系有兩種:客戶(Client)關系和繼續(Inheritance)關系。客戶關系如Figure 2所示,表示一個類(Client)會使用到另一個類(Server)。一般將這種關系中的Client類稱爲客戶端,Server類稱爲服務器。

過程式設計和面向對象設計的比較(組圖)

Figure 2

繼續關系如Figure 3所示,表示一個類(Child)對另一個類(Parent)的繼續。一般將這種關系中的Parent類稱爲父類,Child類稱爲子類。

過程式設計和面向對象設計的比較(組圖)

Figure 3

面向對象設計的過程就是將各個類按以上的兩種關系組合在一起,這兩種關系都非常簡單,不過組合在一起卻能提供強大的設計能力。下面介紹如何利用這兩種關系來劃分程序的不同模塊。

在很多應用中,我們都需要將數據存儲到數據庫中。一個常見的解決手段是使用分層的方法,將程序分爲應用層和存儲層,Figure 4中是這種方法一個不太成熟的設計:

過程式設計和面向對象設計的比較(組圖)

Figure 4

application代表應用層的邏輯,DB代表了數據庫訪問的邏輯,在這個設計中,Application直接調用了DB的服務來將數據存儲到數據庫中。這樣做的缺點是Application對DB有了依靠關系,一旦DB有了任何變化,Application都有可能會受其影響需要改動。當然,假如只是兩個類的話,這種依靠關系根本算不上什麽問題,然而,我們有理由相信,應用層和存儲層都會由不只一個類組成,並都有一定的複雜度,這時它們之間的這種依靠關系就會讓人頭痛了。當程序的模塊越來越多,假如不限制它們之間的聯系,那模塊間的依靠、程序的複雜度和開發維護的難度都會成指數上升。

所幸的是引入面向對象中的繼續關系,我們可以解決這一問題,如圖Figure 5所示:

過程式設計和面向對象設計的比較(組圖)

Figure 5

Persistence是一個接口,其包含了Application存儲所需用到的服務。DB實現了這個接口,Application則調用Persistence中的服務來存儲數據,兩者都只依靠于Persistence。這樣,Application到DB的依靠關系被Persistence這個接口切斷了。因爲接口中只包含了服務,也就是成員函數的聲明,而不包括任何數據和函數的實現,所以Persistence接口的內容會很簡單。在Persistence不變的情況下,Application和DB這兩個模塊都可以自由地進行修改而不影響到對方。就這樣,通過在中間插入接口的方法,程序的模塊被劃分開,依靠關系也變得輕易治理的多。

假如從另一個角度來看Figure 4中的設計,應用層是程序的核心部分,而存儲層則可以看成是實現的細節,這個設計犯了和過程式設計同樣的錯誤,即核心邏輯依靠于具體的實現細節,這樣,當細節變化時,核心邏輯也會受到影響:假如,當我們需要將數據改存到文件或目錄服務中時,按照Figure 4中的設計Application模塊就難免受到影響。在Figure 5的設計中則沒有這一問題:這裏我們可以把Application和Persistence看成是程序的核心邏輯,而Persistence接口的實現(DB)可以看成是程序的細節實現,其依靠關系是細節依靠于核心,當細節變化時核心不會受其影響,這才是一個穩定的依靠關系。遵從這一設計,我們可以在不影響程序核心的情況下,爲程序添加或更改存儲類型,如Figure 6所示:

過程式設計和面向對象設計的比較(組圖)

Figure 6

這裏值得注重的是Persistence和Application被劃分到了一起,這是因爲Persistence中的服務都是根據Application中的需求制定出來的,Persistence和Application的關系比起它和其子類的關系更加緊密。將接口和其調用者放入一個模塊是一個常見的做法,當然,也可以將接口單獨劃分爲一個模塊,這一般是在有多個不同的調用模塊依靠于該接口,或不同模塊間需要更清楚的分離時才這樣做。

例子

接下來我們將通過一個小例子來看看這兩種設計方法的不同:假設我們有一個圖形處理的程序,這個程序需要一個計算各種圖形面積的功能。

假如用過程式的方法來設計這個功能,可以得到下面的這些代碼:

public class Circle {

public double radius;

}

public class Rectangle {

public double width;

public double height;

}

public class Square {

public double side;

}

public class AreaCalculator {

public static double area(Object shape) {

double area = 0.0;

if (shape instanceof Circle) {

Circle c = (Circle) shape;

area = c.radius * PI * PI;

} else if (shape instanceof Square) {

Square s = (Square) shape;

area = s.side * s.side;

} else if (shape instanceof Rectangle) {

Rectangle r = (Rectangle) shape;

area = r.width * r.height;

}

return area;

}

PRivate static final double PI = 3.14;

  本文比較了過程式設計和面向對象設計,主要論述了過程式設計在程序開發中的一些問題及面向對象設計是如何解決這些問題的。      過程式設計      總的來說,過程式的程序設計是一種自上而下的設計方法,設計者用一個main函數概括出整個應用程序需要做的事,而main函數由對一系列子函數的調用組成。對于main中的每一個子函數,都又可以再被精煉成更小的函數。重複這個過程,就可以完成一個過程式的設計。其特征是以函數爲中心,用函數來作爲劃分程序的基本單位,數據在過程式設計中往往處于從屬的位置。      過程式設計的優點是易于理解和把握,這種逐步細化問題的設計方法和大多數人的思維方式比較接近。      然而,過程式設計對于比較複雜的問題,或是在開發中需求變化比較多的時候,往往顯得力不從心。這是因爲過程式的設計是自上而下的,這要求設計者在一開始就要對需要解決的問題有一定的了解。在問題比較複雜的時候,要做到這一點會比較困難,而當開發中需求變化的時候,以前對問題的理解也許會變得不再適用。事實上,開發一個系統的過程往往也是一個對系統不斷了解和學習的過程,而過程式的設計方法忽略了這一點。      在過程式設計的語言中,一般都既有定義數據的元素,如C語言中的結構,也有定義操作的元素,如C語言中的函數。這樣做的結果是數據和操作被分離開,輕易導致對一種數據的操作分布在整個程序的各個角落,而一個操作也可能會用到很多種數據,在這種情況下,對數據和操作的任何一部分進行修改都會變得很困難。      在過程式設計中,main()函數處于一個非常重要的地位。設計者正是在main()函數中,對整個系統進行一個概括的描述,再以此爲起點,逐步細化出整個應用程序。然而,這樣做的一個後果,是輕易將一些較外延和易變化的邏輯(比如用戶交互)同程序的核心邏輯混淆在一起。假設我們編寫一個圖形界面的計算器程序和一個命令行界面的計算器程序,可以想象這兩個版本的main()函數會有很大的差異,由此衍生出來的程序很有可能也會迥然不同,而這兩個版本的程序本應該有很大部分可以共用才對。      過程式設計還有一個問題就是其程序架構的依靠關系問題。一個典型的過程式程序往往如Figure 1所示:    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510383648.jpg[/img][/url]   Figure 1      圖中的箭頭代表了函數間的調用關系,也是函數間的依靠關系。如圖所示,main()函數依靠于其子函數,這些子函數又依靠于更小的子函數,而在程序中,越小的函數處理的往往是細節實現,這些具體的實現,又經常變化。這樣的結果,就是程序的核心邏輯依靠于外延的細節,程序中本來應該是比較穩定的核心邏輯,也因爲依靠于易變化的部分,而變得不穩定起來,一個細節上的小小改動,也有可能在依靠關系上引發一系列變動。可以說這種依靠關系也是過程式設計不能很好處理變化的原因之一,而一個合理的依靠關系,應該是倒過來,由細節實現依靠于核心邏輯才對。      面向對象設計      面向對象是一種自下而上的程序設計方法。不像過程式設計那樣一開始就要用main概括出整個程序,面向對象設計往往從問題的一部分著手,一點一點地構建出整個程序。面向對象設計以數據爲中心,類作爲表現數據的工具,是劃分程序的基本單位。而函數在面向對象設計中成爲了類的接口。      面向對象設計自下而上的特性,答應開發者從問題的局部開始,在開發過程中逐步加深對系統的理解。這些新的理解以及開發中碰到的需求變化,都會再作用到系統開發本身,形成一種螺旋式的開發方式。(在這種開發方式中,對于已有的代碼,常需要運用Refactoring技術來做代碼重構以體現系統的變化。)      和函數相比,數據應該是程序中更穩定的部分,比如,一個網上購物程序,無論怎麽變化,大概都會處理貨物、客戶這些數據對象。不過在這裏,只有從抽象的角度來看,數據才是穩定的,假如考慮這些數據對象的具體實現,它們甚至比函數還要不穩定,因爲在一個數據對象中增減字段在程序開發中是常事。因此,在以數據爲中心構建程序的同時,我們需要一種手段來抽象地描述數據,這種手段就是使用函數。在面向對象設計中,類封裝了數據,而類的成員函數作爲其對外的接口,抽象地描述了類。用類將數據和操作這些數據的函數放在一起,這可以說就是面向對象設計方法的本質。      在面向對象設計中類之間的關系有兩種:客戶(Client)關系和繼續(Inheritance)關系。客戶關系如Figure 2所示,表示一個類(Client)會使用到另一個類(Server)。一般將這種關系中的Client類稱爲客戶端,Server類稱爲服務器。    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510383794.jpg[/img][/url]   Figure 2      繼續關系如Figure 3所示,表示一個類(Child)對另一個類(Parent)的繼續。一般將這種關系中的Parent類稱爲父類,Child類稱爲子類。    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510383859.jpg[/img][/url]   Figure 3      面向對象設計的過程就是將各個類按以上的兩種關系組合在一起,這兩種關系都非常簡單,不過組合在一起卻能提供強大的設計能力。下面介紹如何利用這兩種關系來劃分程序的不同模塊。      在很多應用中,我們都需要將數據存儲到數據庫中。一個常見的解決手段是使用分層的方法,將程序分爲應用層和存儲層,Figure 4中是這種方法一個不太成熟的設計:    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510383908.jpg[/img][/url]   Figure 4      application代表應用層的邏輯,DB代表了數據庫訪問的邏輯,在這個設計中,Application直接調用了DB的服務來將數據存儲到數據庫中。這樣做的缺點是Application對DB有了依靠關系,一旦DB有了任何變化,Application都有可能會受其影響需要改動。當然,假如只是兩個類的話,這種依靠關系根本算不上什麽問題,然而,我們有理由相信,應用層和存儲層都會由不只一個類組成,並都有一定的複雜度,這時它們之間的這種依靠關系就會讓人頭痛了。當程序的模塊越來越多,假如不限制它們之間的聯系,那模塊間的依靠、程序的複雜度和開發維護的難度都會成指數上升。      所幸的是引入面向對象中的繼續關系,我們可以解決這一問題,如圖Figure 5所示:    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510383961.jpg[/img][/url]   Figure 5      Persistence是一個接口,其包含了Application存儲所需用到的服務。DB實現了這個接口,Application則調用Persistence中的服務來存儲數據,兩者都只依靠于Persistence。這樣,Application到DB的依靠關系被Persistence這個接口切斷了。因爲接口中只包含了服務,也就是成員函數的聲明,而不包括任何數據和函數的實現,所以Persistence接口的內容會很簡單。在Persistence不變的情況下,Application和DB這兩個模塊都可以自由地進行修改而不影響到對方。就這樣,通過在中間插入接口的方法,程序的模塊被劃分開,依靠關系也變得輕易治理的多。      假如從另一個角度來看Figure 4中的設計,應用層是程序的核心部分,而存儲層則可以看成是實現的細節,這個設計犯了和過程式設計同樣的錯誤,即核心邏輯依靠于具體的實現細節,這樣,當細節變化時,核心邏輯也會受到影響:假如,當我們需要將數據改存到文件或目錄服務中時,按照Figure 4中的設計Application模塊就難免受到影響。在Figure 5的設計中則沒有這一問題:這裏我們可以把Application和Persistence看成是程序的核心邏輯,而Persistence接口的實現(DB)可以看成是程序的細節實現,其依靠關系是細節依靠于核心,當細節變化時核心不會受其影響,這才是一個穩定的依靠關系。遵從這一設計,我們可以在不影響程序核心的情況下,爲程序添加或更改存儲類型,如Figure 6所示:    [url=/bbs/detail_1758839.html][img]http://image.wangchao.net.cn/it/1323510384126.jpg[/img][/url]   Figure 6      這裏值得注重的是Persistence和Application被劃分到了一起,這是因爲Persistence中的服務都是根據Application中的需求制定出來的,Persistence和Application的關系比起它和其子類的關系更加緊密。將接口和其調用者放入一個模塊是一個常見的做法,當然,也可以將接口單獨劃分爲一個模塊,這一般是在有多個不同的調用模塊依靠于該接口,或不同模塊間需要更清楚的分離時才這樣做。      例子      接下來我們將通過一個小例子來看看這兩種設計方法的不同:假設我們有一個圖形處理的程序,這個程序需要一個計算各種圖形面積的功能。      假如用過程式的方法來設計這個功能,可以得到下面的這些代碼:      public class Circle {   public double radius;   }      public class Rectangle {   public double width;   public double height;   }      public class Square {   public double side;   }      public class AreaCalculator {      public static double area(Object shape) {   double area = 0.0;   if (shape instanceof Circle) {   Circle c = (Circle) shape;   area = c.radius * PI * PI;      } else if (shape instanceof Square) {   Square s = (Square) shape;   area = s.side * s.side;      } else if (shape instanceof Rectangle) {   Rectangle r = (Rectangle) shape;   area = r.width * r.height;   }      return area;   }      PRivate static final double PI = 3.14;
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
王朝網路微信公眾號
微信掃碼關註本站公眾號 wangchaonetcn
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有