函数式编程(FP)和面向对象编程(OOP)直接相比是一种常见的误解。这种比较应该是FP和OOP支持者之间互相怼的产物。

    事实上,FP和OOP是两种不同的看待事物的方式。

    FP强调“everything is lambda”,并且强调在逻辑处理中不变性的重要性。不变到什么地步呢?原教旨主义的FP就连普通的循环都不可以写(因为循环都有个变化的idx或者条件之类的变量),必须用递归实现。这样做的结果就是把一切“状态”都消除。任何“状态”都是由确定的输入经过确定的一组函数处理得到的最终结果。
    OOP强调“everything is object”,以及object之间的消息传递。通过消息传递改变每个Object的内部状态。OOP之父Alan Kay表示”OOP is all about messaging”。利用OOP建模,都会通过某种消息机制来模拟一些场景的处理。比如
    交易=下单Object,支付Object,积分Object等之间进行交互
    当然,实际的OOP的程序运行时为了效率一般会用方法调用,而不是真的传递一个物理消息。
    如果你看懂了上面两个概念,就会发现他们说的事情压根就不在一个频道里。因此各自的好处也不能证明另一方有缺点。
    现在经常看到文章表达FP如何如何优于OOP,大概原因有这么两点:

    第一点是,OOP早期不切实际的吹牛皮,吹爆了。很多人谈起OOP,都会有“用了OOP,代码耦合就小了,就容易维护了,扩展就方便了,代码就更容易复用了等等“的第一印象。但实际上这并不一定发生。软件设计并非因为OOP就直接自动变好了。因此很多程序员在趟坑多年后可能会感觉“我擦,学了这么多年,全是假的“。更进一步的,像Java这样的“纯OOP”语言迫使程序员并不需要OOP的情况下也得照着OOP的方式去写代码,结果啰嗦又臃肿。所以很多人越来越讨厌OOP其实是可以理解的。(比如这篇Goodbye, Objected Oriented Programming)。
    现在的Java程序大量使用反射、lambda等技术,已经不是早期那个单纯OOP语言了。

    第二点是现代程序开始往并发发展。而FP的不可变,没有副作用等特性恰好让并发编程变得不容易出错。并且配合多种并发模型(如CSP、Map Reduce、Fork & Join、Promise等),可以解决很多高并发的问题,显得高、大、上、酷。
    但是,我非常赞同《人月神话》的著名论断——没有银弹。不论OOP还是FP,用好了都可以发挥作用,用不好一样吃瘪。举几个例子,

    一个业务领域建模,其实模拟的就是现实当中的不同角色的人/机构的工作方式。因为如果是人/机构互相协作,就是通过消息来协作的。比如博士生想发文章,先得自己写,写了老板审阅,完事发给期刊编辑,编辑找同行评议,完事发表,发表的结果会收录到某个文献索引数据库。这个过程就是多个独立的“对象”在相互协作的结果。因此OOP在这个层面上对这个流程进行抽象是很合适的。当然你也可以说,这时我用FP的各种动作函数的组织来描述这个过程,也是可以的。但是如果比较一下,这个场景用FP和OOP建模,哪个更容易理解呢?

    再比如,对一组数据做加工,先查询,然后聚合,聚合后排序,再join,再排序,再聚合,再转换(map)得到最终的结果。这个过程,用FP的函数就很自然,因为这一看就是
    result = func1(func2(func3…funcN(x))))
    这时用OOP呢?给每一个步骤建一个class?然后把排序、聚合等操作放在class里?抽象个基类?或者弄个XXXUtils的静态方法集合类?当然都可以做,但是很明显这不是个好的设计。
    再再比如,一个业务流程,就是一组步骤:第一步如何如何,第二部如何如何……。这时用FP和OOP都不能很好的表达问题(可能FP接近点)。这其实是典型的“指令式编程“。如果业务逻辑如此,那么就照着一步一步做就是最好的,而不是抽取函数和不变状态;或者定义一些根本无意义的class。
    说了这么多,其实希望表达的意思是:到底用哪种编程模式,要看问题本身适合哪个。哪个用起来自然,和问题本身特质搭配,那就用哪个。用对了,事半功倍;用错了,就各种纠结拧巴。你希望你一个东西模拟为Object,前提是这个东西本身容易抽象成一个Object;你希望你一个数据可以抽象为一组函数执行的组合,前提是这样理解更自然,更舒服。
    此外,同一个问题可以拆解为不同的层次,不同的层次可以使用各自适合的方式。比如高层的可以OOP,具体到某个执行逻辑里可以用FP或者指令编程。
    —-
    P.S. 关于OOP
    很多教材、资料、文章等会说OOP=封装 + 继承 + 多态。其实这个提法非常的误导人。封装是普遍存在的概念,函数也可以封装。而多态也不限于OOP,接口可以多态,用duck typing也可以多态。只有继承专属于OOP,但是继承只是OOP里一个次要一级的代码复用的特性(而且目前还普遍被认为不靠谱,composite over inheritance)。OOP最关键的特征还是通过消息传递来改变Object内部的状态。
    我的另外一个回答非常详细的解释了OOP到底是什么。