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

声明式编程与函数式编程

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

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

彩色我库布里克(2005)

彩色我库布里克(2005)

这是来自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以来​​(在Java的第一个版本中引入了Math类),这些实用程序类随处可见。 好吧,从技术上讲没有错。 该代码将起作用。 但这不是面向对象的编程。 相反,它是必须的和程序的。 我们在乎吗? 好吧,由您决定。 让我们看看有什么区别。

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

命令式编程的重点是描述一个程序在改变程序状态的语句方面如何运作 。 我们刚刚在上面看到了命令式编程的示例。 这是另一种(这是与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

声明式编程与函数式编程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338252.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言中位运算符有哪些

C语言中位运算符有:位操作是程序设计中对位模式按位或二进制数的一元和二元操作。在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多。在现代架构中, 情况并非如此:位运算的运算速度通常…

jsf表单验证_JSF:在正确的阶段进行验证(了解生命周期)

jsf表单验证嗨,大家好! 尽管标题强调验证一词,但本文实际上是关于JSF生命周期的。 那是因为我相信,真正了解生命周期的最简单方法之一就是通过做出我们一直在做的事情:验证用户输入。 通常,了解所谓的JSF…

java广度优先爬虫示例,【爬虫】广度优先遍历抓取数据概述

这次都是一些纯语言的表达,可能会有点啰嗦,或者有点枯燥,也是对爬虫的一些小小的见解,可能只是一些常见话,哈哈,还是耐心的写完。网络爬虫的整体执行流程:1)确定一个(多个)种子网页2)进行数据内…

if语句的用法是什么

if语句的用法:if语句是指编程语言(包括c语言、C#、VB、java、汇编语言等)中用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一。if语句概述if语句是指编程语言(包…

c语言如何实现玫瑰花

c语言实现玫瑰花的方法:#include #include ?#include #include #include #pragma comment(lib,"winmm.lib")//定义全局变量int rosesize 500;int h -250;//定义结构体struct DOT {double x;double y;double z;double r;double g;};bool calc(double a,…

maven 部署nexus_设置本地Nexus存储库并从Maven部署WAR文件

maven 部署nexusMaven Central充当中央存储库管理器,二进制文件由不同的团队/公司/个人上载并与世界其他地方共享。 就像github和其他对源代码控制非常有效的源代码存储库一样,这些存储库管理器还充当您自己生成的二进制工件的部署目标。 设置本地存储库…

c vector用法是什么

在c 中&#xff0c;vector是一个十分有用的容器&#xff0c;c vector用法是&#xff1a;1、基本操作(1)头文件#include.(2)创建vector对象&#xff0c;vector vec;(3)尾部插入数字&#xff1a;vec.push_back(a);(4)使用下标访问元素&#xff0c;cout<<vec[0]<<endl…

c语言for循环如何打印菱形

c语言for循环打印菱形的方法&#xff1a;使用两个for循环&#xff0c;实现条件判断&#xff0c;代码为【int i,j;for(i0; i<2*n-1; i )_(in-i-1&&jc语言for循环打印菱形的方法&#xff1a;方法一&#xff08;以循环为主打印&#xff09;#include void print(int n) …

quasar 异步回调_Java IO基准测试:Quasar与异步ForkJoinPool与ManagedBlock

quasar 异步回调“ Arien看到了我们运行的parallelStreams和ForkJoin基准测试的结果后&#xff0c;在Twitter上与我们联系。 这激起了他的兴趣&#xff0c;因此他进行了一些自己的测试&#xff0c;将Quasar纤维加入了混合物。 这是他的结果和结论。” –塔基皮&#xff08;Taki…

php微信自动回复机器人,微信自动回复机器人功能怎么实现?

原标题&#xff1a;微信自动回复机器人功能怎么实现&#xff1f;微信自动回复机器人功能怎么实现&#xff1f;最近有不少小伙伴都在询问这个问题。很多人在微信营销的过程中&#xff0c;都会有这样的问题&#xff0c;微信好友太多&#xff0c;想要都在第一时间回复&#xff0c;…

C语言怎么合并两个有序链表

C语言合并两个有序链表的方法&#xff1a;拼接指定的两个有序链表的所有节点即可。例如两个有序链表分别为【1->2->4】和【1->3->4】&#xff0c;合并后的有序链表为【1->1->2->3->4->4】。具体方法&#xff1a;将两个有序链表合并为一个新的有序链…

jira集成开发代码_7种JIRA集成可优化您的Java开发流程

jira集成开发代码有哪些最佳集成可以用来优化JIRA工作流程&#xff1f; 我喜欢寻找在工作流程中提高效率的方法。 看着那些小小的自动化和流畅的流程&#xff0c;我的脸上露出了微笑。 我知道我并不孤单&#xff0c;有时花费更多的时间来获得一点点提升以使其正常工作&#xff…

c语言源程序结构是怎样的?

一个C语言源程序至少一个有main函数&#xff0c;定义函数必须指定 4 个元素&#xff1a;返回类型、函数名、圆括号内的形参表&#xff08;可能为空&#xff09;和函数体。源程序的结构特点&#xff1a;1、一个C语言源程序可以由一个或多个源文件组成。2、每个源文件可由一个或多…

c语言规定在一个源程序中main函数的位置是什么?

一个c程序有且仅有一个main函数&#xff0c;除main函数之外可以有若干个其它的函数&#xff0c;每个函数实现某一特定的操作。C语言规定&#xff0c;在一个源程序中&#xff0c;main函数的位置可以任意。因为&#xff1a;在一个C语言源程序中&#xff0c;程序总是从main函数开始…

jax-ws和jax-rs_使用JAX-RS和Jetty创建Web服务和Rest Server

jax-ws和jax-rs用Java创建WebService非常容易。 将其添加到ServletContainer并将其部署到嵌入式WebServer仅需要几行代码。 让我们创建一个具有两个函数的简单计算器&#xff0c;作为WebService的示例。 计算器将计算任意数量的squareRoot和平方。 它将返回一个简单的JSON响应…

可运行的c语言程序的扩展名为什么?

C语言源程序经过C语言编译程序编译之后&#xff0c;生成一个后缀为“.OBJ”的二进制文件(称为目标文件)&#xff0c;最后还要由称为“连接程序”(link)的软件&#xff0c;把此“.OBJ”文件与c语言提供的各种库函数连接在一起&#xff0c;生成一个后缀“.EXE”的可执行文件。显然…

activemq消息持久化_将ActiveMQ持久消息传递性能提高25倍

activemq消息持久化Apache ActiveMQ&#xff0c;JBoss A-MQ和Red Hat Apache ActiveMQ是一个非常受欢迎的开源消息传递代理&#xff0c;由创建&#xff08;和从事&#xff09; Apache Karaf &#xff0c; Apache Camel &#xff0c; Apache ServiceMix以及许多其他工具的人提供…

c语言实现两个有序链表的合并(代码示例)

c语言实现两个有序链表的合并&#xff1a;现有两个有序单链表&#xff0c;通过代码实现将两个单链表合并为一个有序的新表&#xff0c;要求使用旧表的空间&#xff0c;不能新分配内存#include #include typedef struct List{ int a; struct List *next;}list;void newLis…

hibernate工厂模式_Hibernate锁定模式–乐观锁定模式如何工作

hibernate工厂模式显式乐观锁定 在上一篇文章中 &#xff0c;我介绍了Java持久性锁定的基本概念。 隐式锁定机制可防止丢失更新 &#xff0c;它适用于我们可以主动修改的实体。 尽管隐式乐观锁定是一种广泛使用的技术&#xff0c;但很少有人了解显式乐观锁定模式的内部工作原理…

using用法是什么?

using用法是&#xff1a;1、命名空间using namespace 命名空间;//这样每次使用命名空间中的变量时就不用指定命名空间了注意&#xff1a;头文件中不应有using命名空间的声明2、类型别名&#xff08;C 11&#xff09;using aa double;//等价于typedef double aatypedef double …