`

面向对象之弊,面向过程之优

阅读更多
加个通道:G A E - R P R O X Y, http://www.iteye.com/topic/717232

没想到写了这么多。本文并不是要否定OO,而是要给PO更多的肯定。这篇文字不像想象中那么长,只是些一看便知的代码占了些篇幅,请大家花3分钟看完,这将会是个很有意义的讨论。为了方便大家阅读,我把提纲先列在上面。
1。 我们需要全局变量和函数。
2。 我们需要Callback函数。
3。面向对象的代码在重构和重用上没有面向过程的灵活
引申:数据与逻辑的绑定还是分离?
引申:面向对象曾经辉煌但已褪色的光辉
引申:面向对象最有魅力的地方在哪?
尾声:什么时候用OO,什么时候用PO?

回复ray_linn的C#代码:
http://www.iteye.com/topic/712942?page=2#1580812
---------------------
面向对象在过去的十多年里一直被广泛的宣传,现在已经成为世所公认的比面向过程更优秀的编程模式,但是——过犹不及。

[del]Java将被作为面向对象编程语言的典型来做说明,Python将被作为面向过程的语言来说明,虽然Python也面向对象。[/del]
有人说我是在说Java语言的问题,而不是说OO的问题,所以我把OO的代码也改成了python实现。


1。 我们需要全局变量和函数。

java作为一个典型的面向对象的编程语言,为什么要设static关键字。这从侧面说明,面向对象不是万能的。我们需要全局性的变量、全局性的函数(方法)。

单例的设计模式,是试图用面向对象的方法来做全局性的函数。因为对象只会被创建一次,那该对象的方法事实上就是一个全局函数。即便单例可以用面向对象的方法来解决了全局函数的问题,但要获取单例的实例,我们依然无法避免使用static变量来hold这个实例,无法避免使用static函数来获取这个实例。

2。 我们需要Callback函数。

面向过程的语言会有类似这样的代码:

def some_function(param...)
   //my codes...

addListener('some_event',some_function)

而试图完全对象化的Java语言有一个很尴尬的做法
class MyCallbackImpl:
   def myCallbackMethod(MyParam param,...):
     //My codes...

someObj.addListener('some_event',MyCallbackImpl());

我们可以看出,为了这个回调,我们定义了接口,定义了实现类,并且构造了 MyCallbackImpl的对象,并且降低了代码的可读性。我见过许多对回调很晕的同学,我想不是他们的理解能力问题,而是面向对象的这种做法本身的问题。

3。面向对象的代码在重构和重用上没有面向过程的灵活

比如这样的一段代码:
class MyClassA:
  def methodA(self,ParamA){
    //根据ParamA,this.someField得出返回值

class MyClassB:
  def methodB(self,ParamB):
     //根据ParamA,this.someField得出返回值
...
objA = MyClassA();
objA.methodA(paramA)
objB = MyClassB();
objB.methodB(paramB)


methodA只与paramAmethodA被限定在MyClassA的对象中调用,methodB被限定在MyClassB的对象中调用,这两个方法由于业务范畴的原因被归入相应的Class。让我们来看看这样的代码用面向过程的方式会如何写:
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的功能。
我们先看看,面向过程的代码要做什么修改:
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函数————本质上是一种面向过程的方式。

==================================================================

引申:数据与逻辑的绑定还是分离?
面向对象编程在代码逻辑上是意味着什么?个人认为面向对象在代码逻辑上意味着数据与逻辑的绑定。可以想象成 C的Structure和C的function结合成了Cpp的Class。
面向过程在代码逻辑上意味着什么?个人认为面向过程在代码逻辑上意味着数据与逻辑的分离。
我们经常说MVC,数据、逻辑、视图分离。那么我们在最基本的代码上就不需要这种分离了吗?
程序=数据结构+算法 ,对象也可以理解为数据结构和算法的绑定, 对象更加的接近一个程序的完整结构,而过程则更像一个代码段。
从这个角度看,很难说这是优点或缺点。

引申:面向对象曾经辉煌但已褪色的光辉

面向对象出现之初,还是c语言时代,充满了无层次结构的函数,面向对象给函数带来了归属地,让函数可以被更好的整理。而如今,面向过程的语言,也可以通过包的概念来整理函数的归属。

此外,OO带来访问控制的一些概念,private,protected,public,这些访问控制的确令人眼前一亮,但很难说他还有吸引力。对于访问控制,在编译原理上面向过程的语言同样可以实现,但更重要的还是一个好的编码习惯,比如python的 __ 前缀函数,开发者会自然的规避调用它。

引申:面向对象最有魅力的地方在哪?

个人认为,面向对象最大的吸引力在于他的表现力。看这样一段代码:
class Fish{
  def swim(){
   //the fish swimming

fish=Fish()
fish.swim()

来看面向过程的实现
def swim(fish):
  //the fish swimming

fish = Fish()
swim(fish)

面向对象的代码,我们很直观的看到 fish.swim() 是鱼游泳。而面向过程的代码则是 swim(fish),游泳这条鱼,函数定义也许改做 make_fish_swim(fish) 更合适。

尾声:什么时候用OO,什么时候用PO?

浮在海上的冰山,大部分的内容在海面以下。海面以上的用OO来表现会更美,海面以下的用PO来表现会更合适。

Use OO as data-structure more than design patterns, more static utility functions.
分享到:
评论
24 楼 starsea 2010-07-15  
没有什么思想是万能的,融合各种思想才是王道,但是融合里面应该有个主次之分,举例来说持久层都用hibernate和持久层用hibernate为主+jdbc为辅这两种方案,后者更能提高app的性能。而以OO为主辅之PO现阶段来看比强行都用OO更适合现在的应用开发。
23 楼 jasongreen 2010-07-15  
fight_bird 写道
楼主想要的语言是ActionScript3.0,比楼主想要的还要多。

AS3挺好。
22 楼 fight_bird 2010-07-15  
楼主想要的语言是ActionScript3.0,比楼主想要的还要多。
21 楼 palmer 2010-07-15  
很难同意楼主的说法。
但是也不会给你 投新手贴。

不能为了OO 而OO。
OO是手段, 不是目的。

1。 我们需要全局变量和函数。   但是 这仅仅是一小部分。

2。 我们需要Callback函数。  
我想 函数也可以是对象。不一定非得需要用PO来解决吧?  JavaScript里面函数就是Object, 当然我还没有透彻的理解。正在学习。

OO的语言编译了以后还是 机器代码。 DOS里面都是中断, 其实Windows 最终还是中断, 

load。。。执行。。 中断。。。。。 压栈。。。。   执行。。。出栈。。。。回复。 继续执行。

有时学习的多了,  思考和疑惑就多了,   是因为 还是局限在一个小圈子里思考。

当然,浅薄的人不愿意 思考。

降低复杂度,  提高可读性,  以及代码的效率 有时是矛盾的。  常常需要折中一下。


OO  和 PO 也应该是这样吧。
20 楼 ray_linn 2010-07-15  
jasongreen 写道
ray_linn 写道
恩,Java不是OO语言的全部吧。回调函数在C#里就很处理并不尴尬,(当然用C++搞就更不尴尬了),从楼主的例子上看,只能说Java太固步自封了,在C#出来之前一直没有进步。

class A
{
      public event ProcessDelegate ProcessEvent;
}      

var a =new A(); 
a.ProcessEvent += new ProcessDelegate(t_ProcessEvent);


也可以这样搞
        class A<T>
        {
            public void MethodA(Func<T> fun)
            {
                fun.Invoke();
            }
        }


第二个问题就更简单了,C#中有扩展方法,我们给A,B做个伪接口
interface Fake{}
Class A :Fake{}
Class B :Fake{}
static Class Ext
{
   TypeA MehtodA(this Fake ojb, ParamA){}
   TypeB MethodB(this Fake obj, ParamB){}
}

调用时
A.MethodA(...) A.MethodB(...), B.MethodA(...),B.MethodB(...); 如果问题再具体点,还有更多有趣的方法。




C#真神奇。C#加入这些特性,是件好事。

第一段通过 event关键字,+=操作符让ProcessEvent成为了一个event dispatcher,ProcessDelegate是否就是callback函数?ProcessDelegate是一个类?

但我们是否应该反思,是不是因为OO的设计模式出现了问题,所以才需要加入这些特性的呢?我们是不是更宁愿使用编译器带来的特性帮助我们————用简单的代码(event关键字,+=操作符)————编译出复杂的机制(基于OO的事件分发系统)————来实现简单的功能(其实就用函数指针或引用就行了),而不愿意偶尔改变一下思路,使用PO的方式来实现简单的功能呢?

第二段像反射。
发现越是牛逼的项目,反射用的就越多,反射的本质是什么?反射的目的是进入到OO的结构内部,挖掘所需信息,调用所需函数。对于PO来说,根本就不存在反射这个概念。反射的出现与流行证明了人们尤其是牛逼的人们受不了某种限制,虽然他们也许都没有意识到这种限制来自于OOP。

第三段像是根据this关键字在编译阶段注入方法。
什么叫编译阶段注入方法,就是OO在语法上不允许在2没有继承关系的类上通用的方法,通过在目标代码中由编译器强行插入函数指针的方式来实现无继承关系的2个类的通用。

类似的还有,AOP概念,mock object,其实在PO里面简单的HOOK就可以实现。

其实我们已经知道,总有那么一些情况,我们用类继承,多重继承,没有办法解决问题,所以有了 Adapter,Bridge,Decorator,Facade 等等这些设计模式来帮助我们解决问题,现在C#给了我们另一种选择,编译器增强“模式”,这些都是OO所带来的问题所引申出的概念。

C#带来的编译器增强“模式”,的确是个效率更高的好模式,但会带来语法越来越复杂的问题。


第一、二段都是委托,委托还是类,只是个特殊的类而已,这个类带有同步的invoke方法,和异步的BeginInvoke和EndInvoke方法,由编译器完成。

当然C#最犀利的地方就是dynamic object了,可以照样跑ruby的 a.find_name_all().
19 楼 jasongreen 2010-07-15  
night_stalker 写道
jasongreen 写道
icefishc 写道
喂, 过程和函数是2个东西

随便啦。


这么说吧,OO 是过程的延伸,不能脱离命令式编程而存在,归根到底是 imperative paradigm 的语法糖。
如果脱离了过程语句(if, for, while ...),OO 只能做架构,写不了程序。

相比之下函数式编程就简单多了,不需要 OO 和指令式语句就能自成体系,既能架构,又能程序。

http://www.powerset.com/explore/semhtml/Programming_paradigm

这么说吧,“函数式编程”是属于“面向过程”的一种类似“设计模式”的东西。
18 楼 night_stalker 2010-07-15  
jasongreen 写道
icefishc 写道
喂, 过程和函数是2个东西

随便啦。


这么说吧,OO 是命令式的延伸,不能脱离命令式编程而存在,归根到底是 imperative paradigm 的语法糖。
如果脱离了过程语句(if, for, while ...),OO 只能做架构,写不了程序。

相比之下函数式编程就简单多了,不需要 OO 和指令式语句就能自成体系,既能架构,又能程序。

http://www.powerset.com/explore/semhtml/Programming_paradigm
17 楼 jasongreen 2010-07-15  
ipeer 写道
觉得是例子中代码实现的问题, 换种实现,oo就能体现优点


换种实现是OO常遇到的问题,类结构会被多次“换种实现”,PO虽然第一次累点,但很少需要“换种实现”,所以我说“OO重构麻烦”
16 楼 jasongreen 2010-07-15  
ray_linn 写道
恩,Java不是OO语言的全部吧。回调函数在C#里就很处理并不尴尬,(当然用C++搞就更不尴尬了),从楼主的例子上看,只能说Java太固步自封了,在C#出来之前一直没有进步。

class A
{
      public event ProcessDelegate ProcessEvent;
}      

var a =new A(); 
a.ProcessEvent += new ProcessDelegate(t_ProcessEvent);


也可以这样搞
        class A<T>
        {
            public void MethodA(Func<T> fun)
            {
                fun.Invoke();
            }
        }


第二个问题就更简单了,C#中有扩展方法,我们给A,B做个伪接口
interface Fake{}
Class A :Fake{}
Class B :Fake{}
static Class Ext
{
   TypeA MehtodA(this Fake ojb, ParamA){}
   TypeB MethodB(this Fake obj, ParamB){}
}

调用时
A.MethodA(...) A.MethodB(...), B.MethodA(...),B.MethodB(...); 如果问题再具体点,还有更多有趣的方法。




C#真神奇。C#加入这些特性,是件好事。

第一段通过 event关键字,+=操作符让ProcessEvent成为了一个event dispatcher,ProcessDelegate是否就是callback函数?ProcessDelegate是一个类?

但我们是否应该反思,是不是因为OO的设计模式出现了问题,所以才需要加入这些特性的呢?我们是不是更宁愿使用编译器带来的特性帮助我们————用简单的代码(event关键字,+=操作符)————编译出复杂的机制(基于OO的事件分发系统)————来实现简单的功能(其实就用函数指针或引用就行了),而不愿意偶尔改变一下思路,使用PO的方式来实现简单的功能呢?

第二段像反射。
发现越是牛逼的项目,反射用的就越多,反射的本质是什么?反射的目的是进入到OO的结构内部,挖掘所需信息,调用所需函数。对于PO来说,根本就不存在反射这个概念。反射的出现与流行证明了人们尤其是牛逼的人们受不了某种限制,虽然他们也许都没有意识到这种限制来自于OOP。

第三段像是根据this关键字在编译阶段注入方法。
什么叫编译阶段注入方法,就是OO在语法上不允许在2没有继承关系的类上通用的方法,通过在目标代码中由编译器强行插入函数指针的方式来实现无继承关系的2个类的通用。

类似的还有,AOP概念,mock object,其实在PO里面简单的HOOK就可以实现。

其实我们已经知道,总有那么一些情况,我们用类继承,多重继承,没有办法解决问题,所以有了 Adapter,Bridge,Decorator,Facade 等等这些设计模式来帮助我们解决问题,现在C#给了我们另一种选择,编译器增强“模式”,这些都是OO所带来的问题所引申出的概念。

C#带来的编译器增强“模式”,的确是个效率更高的好模式,但会带来语法越来越复杂的问题。
15 楼 dsea 2010-07-15  
yangguo 写道
可惜不能投新手帖。

随便找几个毫无意义的例子就出来否定OO,可笑可笑。
扩展性和适应变化正是OO最大优点,竟然说不如PO,简直一派胡言。
劝君学习一下设计模式再出来发表高论,免得误人子弟,怡笑大方。


要不,你来说说看...
14 楼 haochong 2010-07-15  
怎么不说说面向对象的面向接口编程的优势啊。
13 楼 ipeer 2010-07-15  
觉得是例子中代码实现的问题, 换种实现,oo就能体现优点
12 楼 hatedance 2010-07-15  
1 全局变量在OO里可以用单例实现
2 回调函数也是“对象”,如果你真的是个OOer
3 OO的长处不是灵活,而是解决复杂问题

OO的根本是要求你有OO的思维,把一切看成对象,特别是问题领域。

11 楼 jasongreen 2010-07-15  
icefishc 写道
喂, 过程和函数是2个东西

随便啦。
10 楼 jasongreen 2010-07-15  
yangguo 写道
可惜不能投新手帖。

随便找几个毫无意义的例子就出来否定OO,可笑可笑。
扩展性和适应变化正是OO最大优点,竟然说不如PO,简直一派胡言。
劝君学习一下设计模式再出来发表高论,免得误人子弟,怡笑大方。


开头就说了,本文并不是要否定OO,而是要给PO更多的肯定。
9 楼 ironsabre 2010-07-15  
yangguo 写道
可惜不能投新手帖。

随便找几个毫无意义的例子就出来否定OO,可笑可笑。
扩展性和适应变化正是OO最大优点,竟然说不如PO,简直一派胡言。
劝君学习一下设计模式再出来发表高论,免得误人子弟,怡笑大方。


这绝对不能算新手贴。反而我觉得你比较新手更多一点。
8 楼 icefishc 2010-07-15  
喂, 过程和函数是2个东西
7 楼 yangguo 2010-07-15  
可惜不能投新手帖。

随便找几个毫无意义的例子就出来否定OO,可笑可笑。
扩展性和适应变化正是OO最大优点,竟然说不如PO,简直一派胡言。
劝君学习一下设计模式再出来发表高论,免得误人子弟,怡笑大方。
6 楼 likeblood 2010-07-15  
如果需要一个有属性状态的东西 就用对象 如果有些方法都是作用在这个或者这些属性上 就是用绑定方法 至少getter setter这些方法还是与数据绑定的

如果方法只用来处理数据 而不关心这些数据来自于哪个对象 那就是过程 一些static的东西完全可以剥离出来 只是和当前所在类定义的东西有且仅有相关的操作 就没必要剥离出来

在我看来所谓的对象就分2-3种来定义就好
1、逻辑对象 处理业务逻辑 本身是有数据状态的
2、应用类 就是static方法的集合类 没有什么属性状态 或者都是些不变的属性 或者是可变但是公用的 这个根本算不得对象
3、这个可以有 也可以无 我通常会放 就是用来和数据库做ORMapping的 通常只有setter getter方法 1的对象要对此作业务上的处理 然后由ORMapping的操作程序负责和数据库进行数据的交换
5 楼 jasongreen 2010-07-15  
yuan 写道

1、首先Java似乎算不上典型的面向对象语言。然后说这个static,Ruby中对应Java的静态方法有类方法,但Ruby的类是个对象,所以类方法也是对象的方法。只是这个对象比较特别,生命周期比较长,作用域相对广些吧……看起来只是个概念定义的问题。对于“我们需要全局性的变量、全局性的函数”,我也觉得是这样

2、Java中事件监听似乎多用匿名类,相对麻烦些,这个似乎只是因为Java中的方法不是一个对象,不能直接当参数传递给某个方法。JavaScript中的function、Ruby里的proc都是一个对象。从你的代码结合我对python粗浅了解过的一丁点印象中,好像python的function也是个对象。


其实想表达的是,全局函数、函数指针、函数引用本质上都是PO的概念。
为了OO而OO的设计出callable对象,个人觉得是一件画蛇添足的事情。

yuan 写道

3、不懂python……仔细看看像是给同一个函数传递不同类型的参数,这是强类型和弱类型的问题,也应该算不上OO和PO的问题吧。

贴的代码造成了这个误解,想表达的是2L dennis_zane的观点。

yuan 写道

这后半段内容在以前我会不以为然,不过学了一段时间JavaScript之后挺同意的。
关于前半段,那个包的概念跟Java的package是不是同一回事?如果是的话,那好像也只能说PO比OO少了些东西……作用域的控制上还是OO可控性好些吧。

是的,PO比OO少了些东西,但PO不再像以前那样一无所有了。

相关推荐

Global site tag (gtag.js) - Google Analytics