函数式编程 vs. 面向对象编程

Posted 谭谭ibio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数式编程 vs. 面向对象编程相关的知识,希望对你有一定的参考价值。


FP 和 OOP 都是前辈们探索出来为更好的维护和协同工作而人为发明的 concept,没有谁好谁坏之分。遇到不同的使用场景,选择最合适的即可。


世界从来都是不是一个维度的。有的人推崇 FP,有的人则是 OOP 的死忠粉。这两种编程思想都很好,没有优劣之分。我本身是从 OOP 开始接触编程的,最近的一些项目中,对 FP 有一些新的认识,下面是我总结的对比。



首先看一看维基百科的函数式编程(functional programming,简称 FP)的概念:

In computer science, functional programming is a programming  paradigm—a style of building the structure and elements of computer   programs—that treats computation as the evaluation of mathematical   functions and avoids changing-state and mutable data.


看了以上的定义,我对 FP 函数式编程的理解主要有两点:

  • 不改变 input

  • 没有 side effect

和面向对象编程(object-oriented programming,简称 OOP)最大的区别就在于,OOP 里子类会继承、改变父类的状态,并且很多时候 method 不是 pure function,会有很多 side effect 产生。

先来看一个财务软件的例子(廖雪峰:函数式编程的核心思想)已经很赞。这里是在这个例子基础上,我理解的 FP 和 OOP 的对比。


01

OOP 举例


先来看看用 OOP 是怎么解决此类问题的。

// 这是初始版本public class IncomeTaxCalculator{ protected double _threshold = 3500;
public double calculate(IncomeRecord record){ double tax = record.salary <= _threshold ? 0 : (record.salary - _threshold) * 0.2; return tax; }}
// 往往 Value Object 一旦发布基本上就很难改变,因为外部已经有很多引用class IncomeRecord{ String id; // 身份证号 String name; // 姓名 double salary; // 工资}
// 当需求改变时 OOP 的处理方法public class IncomeTaxCalculatorV2018 extends IncomeTaxCalculator{
// 2018年9月1号后起征点调整到了5000,重写 calculate method 加上这个逻辑 public double calculate(IncomeRecord record){ if(today() > date(2018, 9, 1)){ double _threshold = 5000; } return super.calculate(record); }}
IncomeTaxCalculator calculator = new IncomeTaxCalculator();calculator.calculate(new IncomeRecord(1234, 'ibio', 10000));
// 需求改变后,只需要使用新的 class 即可:IncomeTaxCalculator calculator2018 = new IncomeTaxCalculatorV2018();calculator2018.calculate(new IncomeRecord(1234, 'ibio', 10000));


不可否认,OOP 对可维护性有非常好的支持,把可维护性带到了一个新的高度。但也有一些弊端。

从以上例子可以看出来原来的 class 完全不需要任何改动,有任何的新需求只需要新增一个 subclass 继承原来的 IncomeTaxCalculator 即可。但这样有几个问题:

  1. subclass IncomeTaxCalculatorV2018.calculate() 包含了 today(),即 side effect,如果不这么做,那就需要改变 IncomeRecord,即 input

  2. parent class 内部变量 _threshold 发生了改变


02

FP 举例


再来看看用 FP 的思想是怎么做的:

// 初始方法function calculator(record){ const threshold = 3500; return record.salary <= threshold ? 0 : (record.salary - _threshold) * 0.2;}
// 应对需求,新增的计算方法function calculatorV2018(record){ const threshold = 5000; return record.salary <= threshold ? 0 : (record.salary - _threshold) * 0.2;}
// 高阶函数 higher-order function ,包装之前的函数function getCalculator(oldFn, newFn, today){ if(today() > date(2018, 9, 1)){ return newFn; }else{ return oldFn; }}
calculator(new IncomeRecord(1234, 'ibio', 10000));// 需求改变后,用高阶函数包装之前的函数const taxCalculatorV2018 = getCalculator(calculator, calculatorV2018, new Date(2018, 9, 1));taxCalculatorV2018(new IncomeRecord(1234, 'ibio', 10000));


当需求改变后,只需要新增加一个  pure function calculatorV2018()
和一个 higher-order function  getCalculator() 来包装之前已经存在的函数
calculator(),使得这些函数都可以变为 pure function。

我认为 FP 最大的优势在于每个函数的 input 和 output 映射,而且期间没有任何 side effect,所以单个函数构造简单,非常容易测试。

但缺点也显而易见,为了应对日益变更的需求,有可能需要不断的有 higher-order function 来包装之前已经存在的 pure function,多层包装之后大大增加了代码复杂度从而将代码变得难以维护和可读性差。

一个经典的例子就是 React 的 higher-irder function components,经常在封装了多层之后,已经弄不清楚哪些变量是属于个 component,以及基本的父子关系等等。


03

总结


FP 和 OOP 都是前辈们探索出来为更好的维护和协同工作而人为发明的 concept,没有谁好谁坏之分。遇到不同的使用场景,选择最合适的即可。


看更多走心好分享

请长按下方图片

函数式编程 vs. 面向对象编程

你若喜欢,为谭谭ibio点个在看 

以上是关于函数式编程 vs. 面向对象编程的主要内容,如果未能解决你的问题,请参考以下文章

面向对象

Python之面向对象面向对象初识

《JavaScript函数式编程思想》——从面向对象到函数式编程

python_函数式编程

面向对象day1

面向对象编程