面向对象在过去的十多年里一直被广泛的宣传,现在已经成为世所公认的比面向过程更优秀的编程模式,但是——过犹不及。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,但这两个类完全是空的,你可以只理解为是一个数据结构而已。现在需求发生了改变,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来表现会更合适。
【编辑推荐】