logo头像

From zero to HERO

浅谈函数式编程

本文于 1479 天之前发表,文中内容可能已经过时。

1. 前言

最近开始忙起来了,写东西的时间越来越少了。这几天开始在 Java 开发中尝试函数式编程风格。所以就写点小东西来分享一下。

2. 什么是函数式编程?

在我看来函数式编程就是把函数作为一等公民来使用。平常我们开发都是在处理数据。面向对象中都在处理类。而函数式编程不是在写函数就是在写函数的路上。我们来看看 Java 中函数式编程的演进之路。我小时候玩过一种廉价的黑白屏游戏机,只能玩俄罗斯方块。这种是不可扩展的。

 public void playGame(){
        //  只能玩俄罗斯方块
 }

这种机器给童年带来了不少的乐趣。后来小伙伴有了一台gameboy,这种掌机的好处在于它可以插卡。一张卡一个游戏,最有名的莫过于 《超级马里奥》 和《打砖块》了。这时候机器是完全可扩展的了。

     public void  playGameboy(GameboyCard card){
           card.getGame().run()
     }

虽然可以扩展但是卡的价格非常贵,当时买卡的渠道还很少。那时候其实就想如果能自己造 gameboy 游戏就好了(然后我就来搞编程?)。理想中的游戏机是我们不关心你什么风格的游戏,只要你能放入符合接口的游戏卡中并且在我这个游戏机中跑就行。

所以我们定义了一个固定的游戏卡接口:

/**
* @author Felordcn
* @since 2019/10/31 22:13
*/
@FunctionalInterface
public interface Card {

   Game  apply();

}

只要符合这种接口的游戏卡都能插到机器中玩:

   /**
    * Fun.
    *
    * @param card the card
    */
   void fun(Card card) {
       Game game = card.apply();
       game.run();
   }

熟练面向对象的同学们会说这不就是面向接口编程吗?是的你说的没有问题。但是这里 Card 接口只干一件事就是提供游戏。我们的重心是游戏卡吗?显然不是!有趣好玩的游戏才是我们的目的所在。于是我们不管他是卡还是光碟甚至网路,只要能提供游戏给我们娱乐都符合我们的需要。

 // 玩插卡游戏机
 fun(() -> new CardGame());
  // 玩 PSP
  fun(() -> new PSPGame());
  // more

作为一名码农,平常我们都在写 SQL。 无论大厂小厂,不管单体还是分布式。SQL 总能帮我们解决很多业务关系处理。SELECTINSERTUPDATEDELETE 每一种命令只要是 SQL 规范数据库,不管是什么表都会是一致的操作。你声明了什么命令就执行什么操作。这时数据与函数是松耦合的。正是这样的特点让我们实现了“万变不离其宗”。这也是一种另类的函数式编程。

3. 面向对象和函数式编程冲突吗?

面向对象一直处于我能操作什么数据、这种数据我该怎么操作的范式中。而函数式编程一直沉浸于给我操作数据的方法中。面向对象最大优点是多态性和封装;函数式编程优势是抽象化和声明式命令风格,两者其实是正交,可互补的,可在同一程序中共存。争论是面向对象好还是面向函数好跟争论哪门语言好一样都是非常极端的。对于面向对象来讲:存在的并不一定都是对象,函数就是对象;对于函数式编程来说:存在的并不总是纯粹的,副作用总是真实存在的。总之,面向对象侧重于分解,函数编程侧重于组合。

4. 函数式编程特点

函数风格的编程拥有自己的一些特点:

  • 函数作为一等公民。 可以作为参数传递、从函数里返回、可以赋值给变量。
  • 带有闭包的 Lambda 表达式和匿名函数,这是广泛的多态。
  • 不变性,大部分无态处理,在函数式程序中,变量是通过外部传入或者申明获得值的。变量不能被改变
  • 基于不可变进而可以无副作用的调用。
  • 通过 tail call 实现递归的性能优化。
  • 提供动态的、可组合的开发思路。

5. 总结

今天简单表达了我对函数式编程的一些理解,对于习惯了面向对象的 Java 开发者来说,理解函数式编程并不容易。它不仅仅有 Lambda 和匿名函数!更多的是一种思想。这里推荐一个很好的 Java 函数式编程库 vavr 。有兴趣的同学可以学习下。

评论系统未开启,无法评论!