解耦
首先讲一个编程的原则,解耦。这个概念都说烂了,但是具体怎么做呢。
解耦其实就是解除依赖。
想想十几年前百花齐放的手机充电线,到现在的Type-C,对消费者的好处就是不用带一大堆不同的充电线了,对企业的好处就是不用为生产特定的接口改生产线。
说回到代码上,要怎么解除依赖,解除什么依赖。
简化代码,就是把逻辑和控制分开先说总结,函数式解决对于状态的依赖,泛型,解决对于类型的依赖。都是对于控制的操作 逻辑,就是指业务逻辑。与语言无关
下面举个例子说明什么是函数式编程,他是如何解除对状态的依赖(说明函数式编程的优势)
它的理念就来自于数学中的代数。
大家耳熟能详的斐波那契数列的函数式表示如下
对于函数式编程来说,它只关心定义输入数据和输出数据相关的关系,对应数学自变量和应变量。
如果写成纯函数,应该是下面这个样子。
这个是你传给我什么,我就返回这个值的 +1 值,你会发现,代码随便拷,而且与线程无关,代码在并行时候不用锁,因为是复制了原有的数据,并返回了新的数据。
再给一个稍微复杂一点的例子
找出偶数;乘以 3;转成字符串返回。
平铺直叙的代码
我需要手动维护nums的数据,代码阅读上如果没有注释,你也会比较晕。
看着是不是有点眼熟,那么这段代码还可以简化
突然间的释怀,好家伙这不就是lambda表达式吗,函数式接口跑哪去了,点进去.map接口看源码
入参其实就是版本2写的函数式接口
减少代码函数只是运用函数式接口附带的,它的主要优势是
特征一、stateless:函数不维护任何状态。函数式编程的核心精神是 stateless,比如上面filter 操作只考虑当前元素的状态,即判断当前元素是否为偶数,不需要考虑其他元素或外部状态的影响,简而言之就是它不能存在状态,打个比方,你给我数据我处理完扔出来。里面的数据是不变的。
特征二、immutable:输入数据是不能动的,动了输入数据就有危险,所以要返回新的数据集。
这两个特征带来的效果就是并行无风险。
在累加的例子中,如果有外部变量也操作了cnt的值,程序固定输入就可能得到不同输出,是有风险的。
apply() 方法是 Java 8 中 Function 接口中的一个方法,它接受一个参数,然后将这个参数应用到函数中,返回一个结果。
下面是一个更复杂的例子,用于解释 apply()和 andThen()方法的用法:
andThen() 方法将 strToInt 和 doubleInt 两个函数串联起来得到了一个新的Function。
它返回一个组合函数,其中参数化函数将首先执行,然后是第一个函数。如果任一函数的计算抛出错误,则会将错误转发给组合函数的调用者。
我理解就是在出入参相同的时候简化了andThen用法
出入参不同的示例
此方法返回一个函数,该函数返回其唯一的参数。
你说,不就原值返回这玩意能有啥用呢
在工作中是个很常见的操作
使用
这里在回顾一遍总结函数式解决对于状态的依赖,泛型,解决对于类型的依赖。无论哪种程序语言,都避免不了一个特定的类型系统。哪怕是可随意改变变量类型的动态类型的语言,我们在读代码的过程中也需要脑补某个变量在运行时的类型。如果每个类型传参都写一个重载方法,不符合复用的原则。上个例子中,<T, K, V>就是对于泛型的运用,这里举一个伪代码的例子
版本1
这里类型绑定了,如果想写女主杀死男主就要创建一个新的重载函数 未来如果我想让男主杀死配角呢,在写一个重载函数太丑陋了我们可以选择对可以传入的角色做一个泛型<角色>男主女主配角都是,这里假设他们没有共同父类和接口
看看还有没可以优化的,牢记解耦控制和逻辑 剧情只有一个杀死太单调了,我想看更多的爱恨情仇怎么办
这样复用性很好了
函数式是不是让你想起linux的shell命令 pipeline 模式
上面的例子是要查看一个用户执行的进程列表,列出来以后,然后取第二列,第二列是它的进程 ID,排个序,再把它显示出来。
程序伪代码可以这么写
我们也可以把函数放进数组里面,然后顺序执行一下。可以尝试实现。