面向对象在过去的十多年里一直被广泛的宣传,现在已经成为世所公认的比面向过程更优秀的编程模式,但是——过犹不及。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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
而试图完全对象化的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());
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
我们可以看出,为了这个回调,我们定义了接口,定义了实现类,并且构造了 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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
这里的面向过程的代码中出现了MyClassA和MyClassB,但这两个类完全是空的,你可以只理解为是一个数据结构而已。现在需求发生了改变,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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
可是面向对象的代码呢?等待他的将是代码的重构,也许他可以选择的重构方式是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()
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
来看面向过程的实现,Python代码:
def swim(fish):
//the fish swimming
fish = Fish()
swim(fish)
def swim(fish):
//the fish swimming
fish = Fish()
swim(fish)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
面向对象的代码,我们很直观的看到 fish.swim() 是鱼游泳。而面向过程的代码则是 swim(fish),游泳这条鱼,函数定义也许改做 make_fish_swim(fish) 更合适。
尾声:什么时候用OO,什么时候用PO?
浮在海上的冰山,大部分的内容在海面以下。海面以上的用OO来表现会更美,海面以下的用PO来表现会更合适。
【编辑推荐】