面向?qū)ο笾?面向過程之優(yōu)
面向?qū)ο?/span>在過去的十多年里一直被廣泛的宣傳,現(xiàn)在已經(jīng)成為世所公認(rèn)的比面向過程更優(yōu)秀的編程模式,但是——過猶不及。Java將被作為面向?qū)ο缶幊陶Z言的典型來做說明,Python將被作為面向過程的語言來說明,雖然Python也面向?qū)ο?/span>。
1、我們需要全局變量和函數(shù)
java作為一個典型的面向?qū)ο蟮木幊陶Z言,為什么要設(shè)static關(guān)鍵字。這從側(cè)面說明,面向?qū)ο蟛皇?**的。我們需要全局性的變量、全局性的函數(shù)(方法)。
單例的設(shè)計(jì)模式,是試圖用面向?qū)ο蟮姆椒▉碜鋈中缘暮瘮?shù)。因?yàn)閷ο笾粫粍?chuàng)建一次,那該對象的方法事實(shí)上就是一個全局函數(shù)。即便單例可以用面向?qū)ο蟮姆椒▉斫鉀Q了全局函數(shù)的問題,但要獲取單例的實(shí)例,我們依然無法避免使用static變量來hold這個實(shí)例,無法避免使用static函數(shù)來獲取這個實(shí)例。
2、我們需要Callback函數(shù)
面向過程的語言會有類似這樣的代碼:
- Python代碼
- def some_function(param...)
- //my codes...
- addListener('some_event',some_function)
- def some_function(param...)
- //my codes...
- addListener('some_event',some_function)
而試圖完全對象化的Java語言有一個很尷尬的做法,Java代碼:
- interface MyCallback{
- MyReturenType myCallbackMethod(MyParam param,...);
- }
- class MyCallbackImpl implement MyCallback{
- MyReturenType myCallbackMethod(MyParam param,...){
- //My codes...
- }
- }
- someObj.addListener(new MyCallbackImpl());
- interface MyCallback{
- MyReturenType myCallbackMethod(MyParam param,...);
- }
- class MyCallbackImpl implement MyCallback{
- MyReturenType myCallbackMethod(MyParam param,...){
- //My codes...
- }
- }
- someObj.addListener(new MyCallbackImpl());
我們可以看出,為了這個回調(diào),我們定義了接口,定義了實(shí)現(xiàn)類,并且構(gòu)造了 MyCallbackImpl的對象,并且降低了代碼的可讀性。我見過許多對回調(diào)很暈的同學(xué),我想不是他們的理解能力問題,而是面向?qū)ο蟮倪@種做法本身的問題。
#p#
3、面向?qū)ο蟮拇a在重構(gòu)和重用上沒有面向過程的靈活
比如這樣的一段代碼,Java代碼:
- class MyClassA{
- TypeA methodA(ParamA){
- //根據(jù)ParamA,this.someField得出返回值
- }
- }
- class MyClassB{
- TypeB methodB(ParamB){
- //根據(jù)ParamA,this.someField得出返回值
- }
- }
- ...
- MyClassA objA = new MyClassA();
- objA.methodA(paramA)
- MyClassB objB = new MyClassB();
- objB.methodB(paramB)
- class MyClassA{
- TypeA methodA(ParamA){
- //根據(jù)ParamA,this.someField得出返回值
- }
- }
- class MyClassB{
- TypeB methodB(ParamB){
- //根據(jù)ParamA,this.someField得出返回值
- }
- }
- ...
- MyClassA objA = new MyClassA();
- objA.methodA(paramA)
- MyClassB objB = new MyClassB();
- objB.methodB(paramB)
methodA只與paramAmethodA被限定在MyClassA的對象中調(diào)用,methodB被限定在MyClassB的對象中調(diào)用,這兩個方法由于業(yè)務(wù)范疇的原因被歸入相應(yīng)的Class。讓我們來看看這樣的代碼用面向過程的方式會如何寫,Python代碼:
- def methodA(paramA,paramField):
- //根據(jù)ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據(jù)ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
- def methodA(paramA,paramField):
- //根據(jù)ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據(jù)ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
這里的面向過程的代碼中出現(xiàn)了MyClassA和MyClassB,但這兩個類完全是空的,你可以只理解為是一個數(shù)據(jù)結(jié)構(gòu)而已?,F(xiàn)在需求發(fā)生了改變,MyClassA需要實(shí)現(xiàn)類似methodB的功能,MyClassB要實(shí)現(xiàn)類似methodA的功能。我們先看看,面向過程的代碼要做什么修改,Python代碼:
- def methodA(paramA,paramField):
- //根據(jù)ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據(jù)ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
- #增加下面的兩句
- methodB(paramA,objA.someField)
- methodA(paramB,objB.someField)
- def methodA(paramA,paramField):
- //根據(jù)ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據(jù)ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
- #增加下面的兩句
- methodB(paramA,objA.someField)
- methodA(paramB,objB.someField)
可是面向?qū)ο蟮拇a呢?等待他的將是代碼的重構(gòu),也許他可以選擇的重構(gòu)方式是static函數(shù)————本質(zhì)上是一種面向過程的方式。
#p#
引申:數(shù)據(jù)與邏輯的綁定還是分離?
面向?qū)ο缶幊淘诖a邏輯上是意味著什么?個人認(rèn)為面向?qū)ο笤诖a邏輯上意味著數(shù)據(jù)與邏輯的綁定??梢韵胂蟪?C的Structure和C的function結(jié)合成了Cpp的Class。
面向過程在代碼邏輯上意味著什么?個人認(rèn)為面向過程在代碼邏輯上意味著數(shù)據(jù)與邏輯的分離。
我們經(jīng)常說MVC,數(shù)據(jù)、邏輯、視圖分離。那么我們在最基本的代碼上就不需要這種分離了嗎?程序=數(shù)據(jù)結(jié)構(gòu)+算法,對象也可以理解為數(shù)據(jù)結(jié)構(gòu)和算法的綁定, 對象更加的接近一個程序的完整結(jié)構(gòu),而過程則更像一個代碼段。從這個角度看,很難說這是優(yōu)點(diǎn)或缺點(diǎn)。
引申:面向?qū)ο笤?jīng)輝煌但已褪色的光輝
面向?qū)ο蟪霈F(xiàn)之初,還是c語言時代,充滿了無層次結(jié)構(gòu)的函數(shù),面向?qū)ο蠼o函數(shù)帶來了歸屬地,讓函數(shù)可以被更好的整理。而如今,面向過程的語言,也可以通過包的概念來整理函數(shù)的歸屬。
此外,OO帶來訪問控制的一些概念,private,protected,public,這些訪問控制的確令人眼前一亮,但很難說他還有吸引力。對于訪問控制,在編譯原理上面向過程的語言同樣可以實(shí)現(xiàn),但更重要的還是一個好的編碼習(xí)慣,比如python的__前綴函數(shù),開發(fā)者會自然的規(guī)避調(diào)用它。
引申:面向?qū)ο笞钣绪攘Φ牡胤皆谀模?
個人認(rèn)為,面向?qū)ο?**的吸引力在于他的表現(xiàn)力。看這樣一段代碼,Java代碼:
- class Fish{
- void swim(){
- //the fish swimming
- }
- }
- Fish fish=new Fish()
- fish.swim()
- class Fish{
- void swim(){
- //the fish swimming
- }
- }
- Fish fish=new Fish()
- fish.swim()
來看面向過程的實(shí)現(xiàn),Python代碼:
- def swim(fish):
- //the fish swimming
- fish = Fish()
- swim(fish)
- def swim(fish):
- //the fish swimming
- fish = Fish()
- swim(fish)
面向?qū)ο蟮拇a,我們很直觀的看到 fish.swim() 是魚游泳。而面向過程的代碼則是 swim(fish),游泳這條魚,函數(shù)定義也許改做 make_fish_swim(fish) 更合適。
尾聲:什么時候用OO,什么時候用PO?
浮在海上的冰山,大部分的內(nèi)容在海面以下。海面以上的用OO來表現(xiàn)會更美,海面以下的用PO來表現(xiàn)會更合適。
【編輯推薦】
- Python繼承體現(xiàn)面向?qū)ο筇卣?/span>
- PHP+Java的開發(fā)經(jīng)驗(yàn):不要太面向?qū)ο?/span>
- 面向?qū)ο笤O(shè)計(jì)原則之單一職責(zé)