函数式编程探索-二-函数式编程与面向对象编程
date
Jan 4, 2020
slug
函数式编程探索-二-函数式编程与面向对象编程
status
Published
tags
JavaScript
函数式编程
summary
type
Post
面向对象编程是当前不可否认的应用最广泛的编程范式,函数式编程则从小众变得越来越热门,它们有哪些不同的地方,我从我了解的知识和使用过程中的感受来列几点。
发展史
函数式编程
实际上要早于面向对象编程,它来源于数学中的 Lambada
表达式,发明它是用来表达数学中演算式,它以逻辑性和简洁的特点,一直流行于学术界。面向对象编程
出现在软件工程化发展进程中,在编程范式中属于命令式,与声明式相对,它的设计目的是为了软件的重用、灵活和扩展,主要特点是封装、继承和多态,在工业界广泛应用。说明:本文讨论的面向对象编程是基于类的面向对象,其实 Javascript
也是面向对象的,它是基于原型的面向对象。
范式
函数式编程
属于声明式
,声明式的设计理念是告诉计算机我需要达到什么目标;面向对象编程
属于命令式
,命令式的设计理念是告诉计算机我需要你做什么操作。从字面上看,这么说很让人迷糊,计算机是一台机器,有什么主动和被动。我理解的是:什么语言都是编译成机器码,不存在主动被动。这里可以理解为我们写代码是给其他人阅读的和调用的,声明式代码看起来是要完成什么目标,比如数组的
filter
,就是按过滤函数过滤一个数组;命令式的代码看起来是要完成一个什么操作,比如 for
,就是执行循环语句。我们先来看一个
Javascript
对比案例,感受一下有什么不同。有这么一个需求,对于下面的
data
数据:- 判断nums是不是空的,空的话找下一个;
- 如果nums不是空,就停,并拿它的id去和后台要资料;
- 如果全部的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
运算。
上述声明式写法已经用到了函数式编程,函数式编程在实践中总结出很多有用的运算,比如
map
,reduce
,filter
。上述命令式写法其实还没用到高级的设计模式,面向对象在大型项目构建时比较有优势。
工程应用
工程应用领域,面向对象编程比较有优势,函数式只在少数领域有大型项目应用。
面向对象
目前大多数的企业服务,都是使用 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是最好的语言”的争论。应该根据实际场景使用,一个项目,可以使用函数式编程,也可以使用面向对象编程,也可以都用,它们都可以满足你的需求。