面向对象与函数编程的比较
最近Bob大叔发表了OO vs FP博文,文章推崇面向对象与函数编程融合。大意翻译如下:
一个朋友在Facebook发布如下一个对比,这激怒了我,如下图:
有很多程序员说过类似的事情, 他们认为面向对象和函数式编程是互斥形式的编程。 函数编程狂热分子好像是象牙塔的高等生命看不起地上穷且天真的OO程序员。而OO程序员也类似一样指责函数语言中括号污染和浪费。
这些观点都是基于OO与FP的无知。
首先要强调几点:
1. OO不是关于状态
对象不是数据结构,对象可能使用数据结构,但是这些数据结构使用方式是隐藏的,这就是为什么数据字段是私有的原因,从对象外部看,你看不见任何状态,所有你看到的都是函数方法,这样对象也是关于函数方法的,不是关于状态。
当对象用作数据结构时,它是一种设计风格,比如Hibernate自称自己是对象-关系数据库的映射,其实不正确,ORM并不是关系数据库和对象的映射,而是关系数据库和数据结构的映射,这些数据结构并不是对象。
对象是一包函数,不是一包数据。
2.函数编程类似OOP是由操作数据的函数组成。
每个函数编程程序都是由操作数据的一系列的函数组成,每个OO程式也是由一系列操作数据的函数组成。
对于OO程序员将函数和数据定义在一起,事实上,所有程序都是函数绑定到数据。
你也许认为这里绑定的方式还是有区别的,但是仔细想想这是很愚蠢的,难道 f(o), o.f(), 和 (f o)有区别吗?难道区别真的是函数调用语法不同吗?
区别
那么OO与 FP的区别是什么?什么是OO有但是FP不能做的,而FP能做的OO则不能的?
1. FP规定了分配的纪律
真正函数编程并没有分配符号,你不能改变变量的状态,确实,变量这个词语在函数编程中属于用词不当,你其实不能改变它们。
函数编程经常会说函数是第一等公民,smalltalk确实是将函数作为一等公民,但是small talk是面向对象语言,不是函数语言。
函数语言与非函数语言的区别是函数语言并不分配statement。
这意味着在函数语言中从来不能改变状态吗?不是,函数语言通常提供某种方式让你改变状态, F允许你定于“可变变量”,Clojure允许你创建一个特殊对象,其状态可以通过魔术的咒语来改变。
关键是一种函数式语言对变化状态通过强加某种仪式施以纪律。 你必须通过正确的方式去做。
2. OO强加函数指针的约束
一些人认为对象是真实世界的对象映射,这样OOP更加贴近我们的思考方式,其实OO真正特点是使用方便的多态性替代了函数指针。
如何实现多态?使用函数指针实现, OO语言简化这种实现,隐藏了函数指针,因为函数指针难以管理得很好,因此这点无疑是值得肯定的,试图使用函数指针如C编写一个多多态代码看看?它们会有复杂不方便的约定,需要遵循“每种情况下”,这通常不现实的。
在Java中,你调用的每个函数都是多态的,你没有办法调用不是多态的函数,那就意味着每个java函数间接地通过函数指针被调用。
如果你需要在C中使用多态,你得自己管理这些指针,这很难,如果你想在Lisp中使用多态性,你也得自己管理指针,将它们作为参数传送到高阶算法(这是一种策略模式),但是在OO语言中,这些指针语言已经帮助你管理,语言会初始化它们 转换marshal 它们 通过它们调用函数。
OO和FP是相互排斥的?
这两者排斥吗?你能有一种语言既能约束变量再分配和函数指针吗?当然,这两者并不是互相排斥的,你可以编写OO-functional程序。
那是否意味着所有OO程序员的设计原则和设计模式都可以被函数程序员使用呢?如果他们确实接受面向对象在函数指针上的约束他们就会。
但是函数编程者为什么要这样做?有什么好处?他们会得到类似OO程序员约束分配的好处吗?
多态好处
多态只有一个好处,而且很大,能够将源码和运行的依赖进行反转。
大多数系统一个函数调用另外一个函数,运行和源码都会发生相同方向的依赖,调用模块依赖于被调用模块,而多态被注射入于两者,那么就存在一个源码依赖的反转,当然运行时刻调用模块还是依赖被调用模块(源码阶段不是,是通过反转或DI依赖注入将依赖的被调用模块注入到调用模块中),源码阶段的调用模块就再也不依赖被调用的源码了,而两者只依赖一个多态接口。
这种反转允许被调用模块扮演一种类似插件作用,确实,这是也是插件工作原理。
插件架构是健壮的,因为稳定的高价值业务规则能够不再依赖于低价值易变化的模块,比如用户接口和数据库(用户接口是高价值,数据库易于变化)
最终的结果是,为了是健壮的系统在重大架构内必须使用多态性。
不变性的好处
不再重新分配变量值的好处是:如果你从来不更新就不会有并发更新会发生的问题。
因为函数编程不再做变量重新分配,可变性被系统特殊仪式照顾,只是预留给非常需要的时候使用。从多个线程和多核角度看这样做本质上是安全的。
函数编程的底线是多处理和多核环境中更加安全。
深层哲学
当然,面向对象和函数式编程信徒们都将抗议我这种简化分析。 他们会认为,有深厚的哲学区别,以及包括心理和数学原因是他们最喜欢的理由。
我的反应是: 呸!
每个人都认为他们是最好的方式。 每个人都是错误的。
设计原则和设计模式?
前面谈到了有人认为几十年以来设计原则和设计模式仅仅适用OO,而函数编程将它们减少到:函数。
这个想法是极端的疯狂。 不管你的编程风格软件设计的原则仍然适用,。 你已经决定使用一种没有一个赋值运算符的语言并不意味着可以忽略单一责任原则;或自动开闭原则。 策略模式对多态性的利用并不意味着该模式不能用于函数式语言。
底线就在这里。 面向对象编程是好的,只有当你知道它是什么。 函数式编程是好的,也只有当你知道它是什么。 函数风格的OO编程也是很好的,一旦你知道它是什么。
[该贴被banq于2014-11-26 09:02修改过]