函数式编程探索(二) - 函数式编程与面向对象编程

面向对象编程是当前不可否认的应用最广泛的编程范式,函数式编程则从小众变得越来越热门,它们有哪些不同的地方,我从我了解的知识和使用过程中的感受来列几点。

发展史

函数式编程 实际上要早于面向对象编程,它来源于数学中的 Lambada 表达式,发明它是用来表达数学中演算式,它以逻辑性和简洁的特点,一直流行于学术界。

面向对象编程 出现在软件工程化发展进程中,在编程范式中属于命令式,与声明式相对,它的设计目的是为了软件的重用、灵活和扩展,主要特点是封装、继承和多态,在工业界广泛应用。

说明:本文讨论的面向对象编程是基于类的面向对象,其实 Javascript 也是面向对象的,它是基于原型的面向对象。

范式

函数式编程 属于 声明式 ,声明式的设计理念是告诉计算机我需要达到什么目标;

面向对象编程 属于 命令式 ,命令式的设计理念是告诉计算机我需要你做什么操作。

从字面上看,这么说很让人迷糊,计算机是一台机器,有什么主动和被动。我理解的是:什么语言都是编译成机器码,不存在主动被动。这里可以理解为我们写代码是给其他人阅读的和调用的,声明式代码看起来是要完成什么目标,比如数组的 filter ,就是按过滤函数过滤一个数组;命令式的代码看起来是要完成一个什么操作,比如 for ,就是执行循环语句。

我们先来看一个 Javascript 对比案例,感受一下有什么不同。

有这么一个需求,对于下面的 data 数据:

  1. 判断nums是不是空的,空的话找下一个;
  2. 如果nums不是空,就停,并拿它的id去和后台要资料;
  3. 如果全部的nums都是空的,就直接拿第一个的id去要资料。
let data = [
  {
    nums: [],
    id: 1
  },
  {
    nums: ['222'],
    id: 3
  },{
    nums: [],
    id: 4
  },{
    nums: ['3213','33'],
    id: 5
  }
]

命令式写法:

if(data.length>0){
  let target = data[0]
  for(let index=0;index<data.length;index++){
    let item = data[index]
    if(item.nums && item.nums.length > 0){
      target = item
      break
    }
  }
}

声明式写法:

const { id } = data.filter(item => item.nums && item.nums.length)[0] || data[0]

看上面两段代码,我们有个直观的感受:

  • 声明式写法直观、简洁;
  • 命令式写法对过程描述很清楚,更便于计算机阅读。

这个例子只是展示两种模式的思维方式的不同,声明式简洁,因为它使用了 filter 运算。

上述声明式写法已经用到了函数式编程,函数式编程在实践中总结出很多有用的运算,比如 mapreducefilter

上述命令式写法其实还没用到高级的设计模式,面向对象在大型项目构建时比较有优势。

工程应用

工程应用领域,面向对象编程比较有优势,函数式只在少数领域有大型项目应用。

面向对象

目前大多数的企业服务,都是使用 Java 编写的,也就是说用的是面向对象的编程模式。可见面向对象的优势还是很明显的。我来列举一下:

  • 建模 人类的认知模式就是渐进式的,从简单到复杂,从具体到抽象。面向对象的接口、类和继承非常容易对我们的认知进行建模,比如,我们做一个电商应用,那么肯定有这么几类对象:商店、商品、顾客、商家和订单等等,我们为每类对象创建一个类去描述它,类和类之间用关系连接起来,非常清晰。而且,商品是分很多品类的,那么我们再用继承来实现重用,基础的设计就这样完成了。再多一点,我们可以加上工厂模式去生产商品对象,非常好。
  • 契约 企业项目一般都是分工协作的,分模块开发,根据契约对内、对外提供服务。面向对象中用接口定义契约非常方便,内部开发定义好契约就明确了代码功能和边界,外部拿到契约就知道如何调用服务。这样做可以实现规范设计、降低耦合和并行开发,大大提高协作效率。
  • 设计模式 设计模式大家都不陌生,或多或少都会用到一些,使用设计模式可以让代码更简洁优美,重用性更好。不过这不能只算面向对象的优势,函数式编程也可以应用设计模式,只不过面向对象的广泛应用积累了很多很成熟的设计模式。

函数式

函数式编程在工程领域相对小众,在计算机理论界比较受欢迎,拥有很多骨灰级玩家。工程领域应用比较多的有 Erlang (爱立信的电信系统就是用它写的,并发性、稳定性特别好),还有 scalar (大数据处理框架 spark 是用它写的)。

函数式编程的优势在第一篇,提到了,主要这几点:

  • 使用表达式,简洁
  • 状态不可变,副作用可控
  • 引用透明

这个概念比较抽象,我们可以来看看一个入门的例子。

首先,定义一个容器,有了这个容器,我们就可以方便的进行表达式运算。

var Container = function(x) {
  this.__value = x;
}

Container.of = function(x) { 
  return new Container(x);
}

Container.prototype.map = function(f){
  return Container.of(f(this.__value))
}

它有什么好处呢?

map

我们来看看示例,比如我们想把一个句子中每个单词的首字母取出来,转成大写字母并拼在一起。

Container.of('The quick brown fox jumps over the lazy dog.')
  .map(s => s.split(/\b/))
  .map(s => s.map(w => w.trim().charAt(0)).join(''))
  .map(s => s.toUpperCase());

总结

函数式编程并不是银弹,也不应该引起类似“Php是最好的语言”的争论。应该根据实际场景使用,一个项目,可以使用函数式编程,也可以使用面向对象编程,也可以都用,它们都可以满足你的需求。

引用

欢迎交流

个人读书公众号,欢迎交流!

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章