回调函数

最近,我被指控反对函数式编程,因为我将实用程序类称为反模式。 绝对是错的! 好吧,我确实认为它们是一种糟糕的反模式,但是它们与函数式编程无关。 我相信有两个基本原因。 首先,函数式编程是声明性的,而实用程序类的方法则是必不可少的。 其次,函数式编程基于lambda演算,其中可以将函数分配给变量。 从这个意义上说,实用程序类方法不是函数。 我将在一分钟内对这些语句进行解码。

在Java中,对于Guava , Apache Commons等人积极推广的这些丑陋的实用程序类,基本上有两种有效的替代方法。 第一个是传统类的使用,第二个是Java 8 lambda 。 现在,让我们看看为什么实用程序类与函数式编程甚至不一样,以及这种误解来自何处。

彩色我库布里克(2005年),布莱恩·库克(Brian W. Cook)

这是来自Java 1.0的实用程序类Math的典型示例:

public class Math {public static double abs(double a);// a few dozens of other methods of the same style
}

当您要计算浮点数的绝对值时,将使用以下方法:

double x = Math.abs(3.1415926d);

它出什么问题了? 我们需要一个函数,并且可以从Math类中获得它。 该类内部有许多有用的函数,可用于许多典型的数学运算,例如计算最大值,最小值,正弦,余弦等。 只看任何商业或开源产品。 自从发明Java以来​​,这些实用程序类到处都有使用(此Math类是在Java的第一个版本中引入的)。 好吧,从技术上讲,没有错。 该代码将起作用。 但这不是面向对象的编程。 相反,它是必须的和程序的。 我们在乎吗? 好吧,由您决定。 让我们看看有什么区别。

基本上有两种不同的方法:声明式和命令式。

命令式编程的重点是描述一个程序在改变程序状态的语句方面如何运作。 我们刚刚在上面看到了命令式编程的示例。 这是另一个(这是与OOP无关的纯命令式/过程式编程):

public class MyMath {public double f(double a, double b) {double max = Math.max(a, b);double x = Math.abs(max);return x;}
}

声明式编程的重点是程序应该完成什么不规定如何做到这一点的动作序列方面采取。 这就是相同的代码在Lisp(一种功能编程语言)中的样子:

(defun f (a b) (abs (max a b)))

有什么收获? 语法不同吗? 并不是的。

命令式和声明式之间的区别有很多定义,但是我会尽力而为。 在场景中,与该f函数/方法相互作用的角色基本上是三个:买主,结果打包者和结果消费者。 假设我这样调用此函数:

public void foo() {double x = this.calc(5, -7);System.out.println("max+abs equals to " + x);
}
private double calc(double a, double b) {double x = Math.f(a, b);return x;
}

在这里,方法calc()是买方,方法Math.f()是结果的打包程序,而方法foo()是消费者。 无论使用哪种编程风格,始终都有这三个人参与其中:买方,包装商和消费者。

假设您是买家,并且想为您的(女友)朋友购买礼物。 第一种选择是去一家商店,支付50美元,让他们为您包装香水,然后将其交付给朋友(并得到一个吻)。 这是当务之急的风格。

第二种选择是去一家商店,支付50美元,并获得一张礼品卡。 然后,您将此卡片出示给朋友(并得到一个吻)。 当他或她决定将其转换为香水时,他或她将参观商店并购买。 这是一种声明式样式。

看到不同?

在第一种情况下,当务之急是,您迫使包装商(美容店)找到库存的香水,将其包装,然后将其作为即用型产品展示给您。 在第二种情况下,这是声明性的,您只是从商店得到了一个承诺,即最终,在必要时,工作人员将找到库存的香水,将其包装,然后提供给需要的人。 如果您的朋友从不使用该礼品卡去商店,香水将保留在库存中。

此外,您的朋友可以将该礼品卡用作产品本身,而无需访问商店。 他或她可以代之以将其作为礼物赠送给其他人,或仅将其换成另一张卡或产品。 礼品卡本身就是产品!

因此,区别在于消费者所得到的-是准备使用的产品(必须)或该产品的凭证,以后可以将其转换为真实的产品(说明性)。

实用程序类(例如JDK中的Math或Apache Commons中的StringUtils返回准备立即使用的产品,而Lisp和其他功能语言中的函数返回“凭单”。 例如,如果您在Lisp中调用max函数,则只有在您真正开始使用它时,才计算两个数字之间的实际最大值:

(let (x (max 1 5))(print "X equals to " x))

在此print实际开始将字符输出到屏幕之前, max函数将不会被调用。 当您尝试“购买” 15之间的最大值时,此x是返回给您的“凭证”。

但是请注意,将Java静态函数一个嵌套到另一个嵌套并不会使它们具有声明性。 该代码仍然势在必行,因为它的执行可以在此处和现在提供结果:

public class MyMath {public double f(double a, double b) {return Math.abs(Math.max(a, b));}
}

“好吧,”您可能会说,“我明白了,但是为什么声明式风格比命令式风格更好? 有什么大不了的?” 我明白了。 首先让我展示一下函数编程中的函数与OOP中的静态方法之间的区别。 如上所述,这是实用程序类和函数式编程之间的第二大区别。

在任何函数式编程语言中,您都可以这样做:

(defun foo (x) (x 5))

然后,以后可以将其称为x

(defun bar (x) (+ x 1)) // defining function bar
(print (foo bar)) // passing bar as an argument to foo

就函数式编程而言,Java中的静态方法不是函数。 您无法使用静态方法执行此类操作。 您可以将静态方法作为参数传递给另一个方法。 基本上,静态方法是过程,或者简而言之,是以唯一名称分组的Java语句。 访问它们的唯一方法是调用过程并将所有必需的参数传递给该过程。 该过程将计算出一些内容,并返回立即可以使用的结果。

现在我们可以听到最后一个问题,我可以听到你问:“好吧,实用程序类不是函数式编程,但是它们看起来像函数式编程,它们运行非常快,并且非常易于使用。 为什么不使用它们? 当20年的Java历史证明实用程序类是每个Java开发人员的主要工具时,为什么要追求完美?”

除了我经常被指控的OOP原教旨主义外,还有一些非常实际的原因(顺便说一句,我是OOP原教旨主义者):

可测试性。 实用程序类中对静态方法的调用是硬编码的依赖关系,出于测试目的,它们永远不会被破坏。 如果您的班级正在调用FileUtils.readFile() ,那么在不使用磁盘上实际文件的情况下,我将永远无法对其进行测试。

效率。 实用程序类由于其强制性而比它们的声明性替代要低得多。 他们只是在此时此地进行所有计算,甚至在不需要处理器资源的情况下也是如此。 StringUtils.split()不会返回将字符串分解成块的承诺,而是立即将其分解。 即使“买方”只要求第一个,它也将其分解为所有可能的块。

可读性。 实用程序类往往很大(尝试从Apache Commons读取StringUtilsFileUtils的源代码)。 实用程序类中缺少关注点分离的整个想法,这使OOP如此美观。 他们只是将所有可能的过程放入一个巨大的.java文件中,当它超过十二种静态方法时,该文件将变得绝对无法维护。

最后,让我重申一下:实用程序类与函数式编程无关。 它们只是静态方法的包,这是命令程序。 无论您要声明多少个物体,又要缩小多少物体,都应尽量远离它们并使用坚固的,有凝聚力的物体。

翻译自: https://www.javacodegeeks.com/2015/03/utility-classes-have-nothing-to-do-with-functional-programming.html

回调函数

回调函数_实用程序类与函数式编程无关相关推荐

  1. 声明式编程与函数式编程_实用程序类与函数式编程无关

    声明式编程与函数式编程 最近,我被指控反对函数式编程,因为我将实用程序类称为反模式 . 绝对是错的! 好吧,我确实认为它们是一个糟糕的反模式,但是它们与函数式编程无关. 我相信有两个基本原因. 首先, ...

  2. 实用程序类与函数式编程无关

    最近,我被指控反对函数式编程,因为我将实用程序类称为反模式 . 绝对是错的! 好吧,我确实认为它们是一种糟糕的反模式,但是它们与函数式编程无关. 我相信有两个基本原因. 首先,函数式编程是声明性的,而 ...

  3. python编写函数_浅谈Python 函数式编程

    匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...

  4. c++ 一个函数包括多个返回值判断_轻松玩转函数式编程

    最近和一些同学讨论了函数式编程,很多同学总觉得听起来很高大上,但用起来却无从下手.于是我抽时间捋了捋,将平时工作中用到的函数式编程案例和思想整理了出来,相信阅读本文后,大家都能快速上手函数式编程. 函 ...

  5. java socket 回调函数_请问Java网络编程如何在不使用多线程的情况下实现异步返回?...

    我指的是在不使用多线程的情况下进行并发处理 具体的情况是,在不使用多线程的情况下,服务器侦听某个端口,在有连接进来的时候会调用某个函数对此连接进行处理,但是由于处理的过程可能会比较长,为了不让后面连接 ...

  6. python积木式编程_实例讲解python函数式编程

    函数式编程是使用一系列函数去解决问题,按照一般编程思维,面对问题时我们的思考方式是"怎么干",而函数函数式编程的思考方式是我要"干什么". 至于函数式编程的特点 ...

  7. Python从入门到精通_第5课_文件访问与函数式编程入门_笔记

    文章目录 文本文件读写3种方法 第一种方法 第二种方法 第三种方法 二进制文件读写 文件和目录操作 基本操作 环境变量 操作文件与目录 序列化和反序列化 序列化 反序列化 用JSON实现序列和反序列化 ...

  8. python采用函数编程模式_浅谈Python 函数式编程

    匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...

  9. oracle 删除函数对象不存在_Python 函数式编程指北,不只是面向对象哦

    了解在Python中如何使用 lambda, map, filter 和 reduce 函数来转换数据结构 Photo by Markus Spiske on Unsplash 面向对象的编程通过封装 ...

最新文章

  1. linux 删除乱码的文件夹,Linux服务器删除乱码文件和文件夹的方法
  2. css-3秒(大概吧...)快速撸出YY游戏页面(三)
  3. Python结合Tableau,万字长文搞定传统线下连锁店数据分析
  4. 路径规划;a*算法 demo_路径规划A*算法
  5. 用 Visual Studio 2019 编译 FFmpeg 简单教程
  6. Python -- 扫描局域网活跃IP
  7. 390计算机毕业设计
  8. 车载网络测试 - 车载以太网 - ICMP详细解析
  9. 【文智背后的奥秘】系列篇——自动文本分类
  10. acrobat PDF删除部分_PDF原来可以这么玩
  11. ASK调制的matlab代码
  12. 微信和淘宝扫码登录背后的实现原理
  13. WebRtc以Trickle ICE形式去进行pair
  14. Hive秒数转成时分秒
  15. 一、操作系统的概念功能
  16. java 虚拟机(JVM)
  17. 软考高级系统分析师知识点整理
  18. 搜索引擎蜘蛛爬虫 User Agent 一览(便于采集)
  19. [C#] StringBuilder简介及使用方法
  20. VMware中将虚拟机的网络类型设置为桥接模式

热门文章

  1. Javascript 未结束的字符串常量
  2. 这种口令解决方案可替代多因子验证
  3. struts2中解决下载文件名中文乱码问题
  4. linux磁盘虚拟化
  5. SQL点滴12—SQL Server备份还原数据库中的小把戏
  6. 听研二师兄师姐报告收获
  7. jstack分析cpu占用100%
  8. tcp时间戳 引起的网站不能访问
  9. python 网络相关依赖库 dpkt、scapy、pcap 安装
  10. CentOS7使用systemctl添加自定义服务