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

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

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

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

彩色我库布里克(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函数将不会被调用。 x是您尝试“购买” 15之间的最大值时返回给您的“凭单”。

但是请注意,将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/360651.shtml

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

相关文章

freeredius3.0 mysql_EDIUS视频采集卡 STROM 3G HD/HD SDI

EDIUS STROM 3G HD/HD SDI高清非编系统视音频采集卡STORM 3G? 适用于视频专业人士,满足基于SDI编辑和无带化工作流程,同时可以在低成本的HDMI监/视器上预监。基于PCIe插口类型的STORM 3G解决方案包括EDIUS?非线性编辑软件,3G HD…

以编程方式确定Java类的JDK编译版本

当需要确定使用哪个JDK版本来编译特定的Java .class文件时, 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在博客文章Autoboxing,Unboxing和NoSuchMethodError中引用了这种方法,但是在继续以编程方式实现此方法之前&…

如何使用Spring Security和Basic身份验证保护Jersey REST服务

在我之前的博客文章“ 检查REST API是否有效的快速方法–从清单文件中获取GET详细信息”中 ,我展示了如何开发REST资源以轻松检查开发的REST API是否可用。 在本文中,我将介绍如何使用Spring Security和基本身份验证来保护此资源的安全性– “在HTTP事务…

python提取数据段_python提取数据段 python数据分析

如何在python中用slice分段取数据?执行以下操作:&gt&gt arange(6)&gt&gt a[0,1,2,3,4,5]&gt&gt a[0:3],a[5][[2,3,4,4&a…

一个JAXB Nuance:字符串与枚举(受限制的XSD字符串)

尽管用于XML绑定的Java体系结构 ( JAXB )在名义情况下(尤其是自Java SE 6以来) 相当容易使用,但它也存在许多细微差别。 一些常见的细微差别是由于无法将 XML模式定义 (XSD)类型与Java 类型精确…

休眠锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改。 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的使用模式。 使用LockModeType.OPTIMISTIC ,将在当前正在运…

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

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

mac solr mysql 配置文件_Solr配置文件浅析

接上一篇Linux下安装solr7.4,来谈谈solr的配置文件schema.xml和db-data-config.xml首先看schema.xml:idfield标签用来定义solr core中的字段。这里列出的三个字段如果没有特殊原因尽量保留。字段id被声明为uniqueKey,是让id来唯一标明一个solrdocument。…

JSF:在正确的阶段进行验证(了解生命周期)

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

OpenShift v3:使用WildFly和MySQL的Java EE 7入门

OpenShift是Red Hat的开源PaaS平台。 OpenShift v3 (将于今年发布)将提供使用Docker和Kubernetes运行微服务的整体体验。 以经典的Red Hat方式,所有工作都在OpenShift Origin的开源中完成。 这也将推动OpenShift Online和OpenShift Enterpris…

mySQL日期函数并运行_mysql日期相关的函数

1、获取当前时间:/**获得当前日期时间(date time)函数:now(), 常用**/select now() fromdual;/**获取当前时间戳,current_timestamp或者current_timestamp()**/select current_timestamp, current_timestamp() fromdual;/**获得当前日期时间…

序列化对象C++对象的JSON序列化与反序列化探索

新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,欢迎批评指正 一:背景 作为一名C开发人员,我始终很期待能够像C#与JAVA那样,可以省力的进行对象的序列化与反序列化,但到现在为止&#xff…

python socket udp并发_Python进阶----UDP协议使用socket通信,socketserver模块实现并发

Python进阶----UDP协议使用socket通信,socketserver模块实现并发一丶基于UDP协议的socket实现UDP协议传输数据代码如下:👇### 客户端# -*-coding:utf-8-*-# Author:Dsimport socket# 实例化UDP协议的socket对象 ,配置参数, socket.SOCK_DGRAM(数据报)udp_clisocket.…

Java IO基准测试:Quasar与异步ForkJoinPool与ManagedBlock

“ Arien看到了我们运行的parallelStreams和ForkJoin基准测试的结果后,在Twitter上与我们联系。 这激起了他的兴趣,因此他进行了一些自己的测试,将Quasar纤维加入了混合物。 这是他的结果和结论。” –塔基皮(Takipi)A…

WP8开发札记(一)WP8应用生命周期管理

在介绍生命周期前,我们先了解两个相关的概念。 1、墓碑机制:WP8与Android采用的真后台机制不同,WP8采用的是墓碑机制。一旦从当前应用程序离开(非退出),该应用会被墓碑化,这样可以更好的管理&am…

python类继承中构造方法_第8.3节 Python类的__init__方法深入剖析:构造方法与继承详解...

第8.3节Python类的__init__方法深入剖析:构造方法与继承详解一、 引言上两节介绍了构造方法的语法及参数,说明了构造方法是Python的类创建实例后首先执行的方法,并说明如果类没有重写构造方法,Python将会给出默认的__init__方法…

OpenShift DIY:使用Gradle构建Spring Boot / Undertow应用程序

由于此bug, Gradle 1.6是在OpenShift上运行的最后一个受支持的Gradle版本。 但是从Gradle 2.2开始,这不再是问题,因此使用自己动手做墨盒在OpenShift上运行最新的Gradle不再是问题。 DIY墨盒是一种实验性墨盒,它提供了一种在OpenS…

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

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

maya 中使用节点连接来求余数:

绑个东西要用到求余,不喜欢用表达式,就想用节点连出来,找了下网上只有 镀金铆钉 在火星时代上的教程,不过不能下载了,就自己想了下,终于搞出来了,做下笔记,不要忘了。 求余的思路&a…

java web 登录界面案例_【JavaWeb】74:写一个登录案例

今天是刘小爱自学Java的第74天。感谢你的观看,谢谢你。话不多说,开始今天的学习:Java又常被称之为后台开发。什么叫后台呢?除了后台还有什么前台、前端后端……这些概念一大堆,还容易弄混。以一个三层架构的知识点来引…