面向對象之弊 面向過程之優
面向對象在過去的十多年里一直被廣泛的宣傳,現在已經成為世所公認的比面向過程更優秀的編程模式,但是——過猶不及。Java將被作為面向對象編程語言的典型來做說明,Python將被作為面向過程的語言來說明,雖然Python也面向對象。
1、我們需要全局變量和函數
java作為一個典型的面向對象的編程語言,為什么要設static關鍵字。這從側面說明,面向對象不是***的。我們需要全局性的變量、全局性的函數(方法)。
單例的設計模式,是試圖用面向對象的方法來做全局性的函數。因為對象只會被創建一次,那該對象的方法事實上就是一個全局函數。即便單例可以用面向對象的方法來解決了全局函數的問題,但要獲取單例的實例,我們依然無法避免使用static變量來hold這個實例,無法避免使用static函數來獲取這個實例。
2、我們需要Callback函數
面向過程的語言會有類似這樣的代碼:
- 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());
我們可以看出,為了這個回調,我們定義了接口,定義了實現類,并且構造了 MyCallbackImpl的對象,并且降低了代碼的可讀性。我見過許多對回調很暈的同學,我想不是他們的理解能力問題,而是面向對象的這種做法本身的問題。
#p#
3、面向對象的代碼在重構和重用上沒有面向過程的靈活
比如這樣的一段代碼,Java代碼:
- class MyClassA{
- TypeA methodA(ParamA){
- //根據ParamA,this.someField得出返回值
- }
- }
- class MyClassB{
- TypeB methodB(ParamB){
- //根據ParamA,this.someField得出返回值
- }
- }
- ...
- MyClassA objA = new MyClassA();
- objA.methodA(paramA)
- MyClassB objB = new MyClassB();
- objB.methodB(paramB)
- class MyClassA{
- TypeA methodA(ParamA){
- //根據ParamA,this.someField得出返回值
- }
- }
- class MyClassB{
- TypeB methodB(ParamB){
- //根據ParamA,this.someField得出返回值
- }
- }
- ...
- MyClassA objA = new MyClassA();
- objA.methodA(paramA)
- MyClassB objB = new MyClassB();
- objB.methodB(paramB)
methodA只與paramAmethodA被限定在MyClassA的對象中調用,methodB被限定在MyClassB的對象中調用,這兩個方法由于業務范疇的原因被歸入相應的Class。讓我們來看看這樣的代碼用面向過程的方式會如何寫,Python代碼:
- def methodA(paramA,paramField):
- //根據ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
- def methodA(paramA,paramField):
- //根據ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據ParamB,paramField得出返回值
- class MyClassA{
- }
- class MyClassB{
- }
- ...
- objA = MyClassA()
- objB = MyClassB()
- methodA(paramA,objA.someField)
- methodB(paramB,objB.someField)
這里的面向過程的代碼中出現了MyClassA和MyClassB,但這兩個類完全是空的,你可以只理解為是一個數據結構而已?,F在需求發生了改變,MyClassA需要實現類似methodB的功能,MyClassB要實現類似methodA的功能。我們先看看,面向過程的代碼要做什么修改,Python代碼:
- def methodA(paramA,paramField):
- //根據ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據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):
- //根據ParamA,paramField得出返回值
- def methodB(paramB,paramField):
- //根據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)
可是面向對象的代碼呢?等待他的將是代碼的重構,也許他可以選擇的重構方式是static函數————本質上是一種面向過程的方式。
#p#
引申:數據與邏輯的綁定還是分離?
面向對象編程在代碼邏輯上是意味著什么?個人認為面向對象在代碼邏輯上意味著數據與邏輯的綁定。可以想象成 C的Structure和C的function結合成了Cpp的Class。
面向過程在代碼邏輯上意味著什么?個人認為面向過程在代碼邏輯上意味著數據與邏輯的分離。
我們經常說MVC,數據、邏輯、視圖分離。那么我們在最基本的代碼上就不需要這種分離了嗎?程序=數據結構+算法,對象也可以理解為數據結構和算法的綁定, 對象更加的接近一個程序的完整結構,而過程則更像一個代碼段。從這個角度看,很難說這是優點或缺點。
引申:面向對象曾經輝煌但已褪色的光輝
面向對象出現之初,還是c語言時代,充滿了無層次結構的函數,面向對象給函數帶來了歸屬地,讓函數可以被更好的整理。而如今,面向過程的語言,也可以通過包的概念來整理函數的歸屬。
此外,OO帶來訪問控制的一些概念,private,protected,public,這些訪問控制的確令人眼前一亮,但很難說他還有吸引力。對于訪問控制,在編譯原理上面向過程的語言同樣可以實現,但更重要的還是一個好的編碼習慣,比如python的__前綴函數,開發者會自然的規避調用它。
引申:面向對象最有魅力的地方在哪?
個人認為,面向對象***的吸引力在于他的表現力。看這樣一段代碼,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()
來看面向過程的實現,Python代碼:
- def swim(fish):
- //the fish swimming
- fish = Fish()
- swim(fish)
- def swim(fish):
- //the fish swimming
- fish = Fish()
- swim(fish)
面向對象的代碼,我們很直觀的看到 fish.swim() 是魚游泳。而面向過程的代碼則是 swim(fish),游泳這條魚,函數定義也許改做 make_fish_swim(fish) 更合適。
尾聲:什么時候用OO,什么時候用PO?
浮在海上的冰山,大部分的內容在海面以下。海面以上的用OO來表現會更美,海面以下的用PO來表現會更合適。
【編輯推薦】