Java注释是一个大错误

注释是在Java 5中引入的,我们都为之兴奋。 如此出色的工具可以缩短代码! 不再有Hibernate / Spring XML配置文件! 只是注释,就在我们需要它们的代码中。 没有更多的标记接口 ,只有运行时保留的 反射可发现注释! 我也很兴奋。 此外,我制作了一些开源库,这些库大量使用注释。 以jcabi-aspects为例。 但是,我不再感到兴奋。 而且,我相信注释是Java设计中的一个大错误。

长话短说,注释存在一个大问题-它们鼓励我们在对象 外部实现对象功能,这与封装的原理背道而驰 。 该对象不再是固体,因为它的行为不是完全由其自己的方法定义的-它的某些功能保留在其他地方。 为什么不好? 让我们看几个例子。

@Inject

假设我们使用@Inject注释属性:

import javax.inject.Inject;
public class Books {@Injectprivate final DB db;// some methods here, which use this.db
}

然后我们有一个注入器,它知道要注入什么:

Injector injector = Guice.createInjector(new AbstractModule() {@Overridepublic void configure() {this.bind(DB.class).toInstance(new Postgres("jdbc:postgresql:5740/main"));}}
);

现在我们正在做的类的实例Books通过容器:

Books books = injector.getInstance(Books.class);

Books类不知道如何以及谁将类DB实例注入其中。 这将在幕后和无法控制的地方发生。 注射即可。 看起来很方便,但是这种态度会对整个代码库造成很大的损害。 控件丢失(不是倒置,而是丢失!)。 该对象不再负责。 它不能对发生的事情负责。

相反,这是应该如何做:

class Books {private final DB db;Books(final DB base) {this.db = base;}// some methods here, which use this.db
}

本文说明了为什么首先要使用依赖注入容器是一个错误的主意: 依赖注入容器是代码污染者 。 注释基本上激发了我们制造容器并使用它们。 我们将功能移出对象之外,然后将其放入容器或其他地方。 那是因为我们不想一遍又一遍地重复相同的代码,对吗? 没错,复制是不好的,但是将对象撕裂甚至更糟。 更糟 对于ORM(JPA / Hibernate),也正是如此,在其中正在积极使用注释。 检查这篇文章,它解释了ORM的问题: ORM是一种进攻性的反模式 。 注释本身并不是主要动机,但它们通过将对象撕裂并在不同位置保留零件来帮助我们和鼓励我们。 它们是容器,会话,管理器,控制器等。

@XmlElement

要将POJO转换为XML时,这就是JAXB的工作方式 。 首先,将@XmlElement批注附加到getter:

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Book {private final String title;public Book(final String title) {this.title = title;}@XmlElementpublic String getTitle() {return this.title;}
}

然后,创建一个编组器,并要求它将Book类的实例转换为XML:

final Book book = new Book("0132350882", "Clean Code");
final JAXBContext ctx = JAXBContext.newInstance(Book.class);
final Marshaller marshaller = ctx.createMarshaller();
marshaller.marshal(book, System.out);

谁在创建XML? 不是book 。 课堂以外的其他人Book 。 这是非常错误的。 相反,这是应该完成的方式。 首先,不了解XML的类:

class DefaultBook implements Book {private final String title;DefaultBook(final String title) {this.title = title;}@Overridepublic String getTitle() {return this.title;}
}

然后,将其打印到XML的装饰器 :

class XmlBook implements Book{private final Book origin;XmlBook(final Book book) {this.origin = book;}@Overridepublic String getTitle() {return this.origin.getTitle();}public String toXML() {return String.format("<book><title>%s</title></book>",this.getTitle());}
}

现在,为了以XML 印刷书籍,我们执行以下操作:

String xml = new XmlBook(new DefaultBook("Elegant Objects")
).toXML();

XML打印功能位于XmlBook 。 如果您不喜欢装饰器的想法,可以将toXML()方法移至DefaultBook类。 这并不重要。 重要的是,功能始终位于对象内部,即位于对象所属的位置。 只有对象知道如何将自己打印到XML。 没有人!

@RetryOnFailure

这是一个示例(来自我自己的库 ):

import com.jcabi.aspects.RetryOnFailure;
class Foo {@RetryOnFailurepublic String load(URL url) {return url.openConnection().getContent();}
}

编译后,我们运行一个所谓的AOP编织器 ,该编织器从技术上将我们的代码转换为如下形式:

class Foo {public String load(URL url) {while (true) {try {return _Foo.load(url);} catch (Exception ex) {// ignore it}}}class _Foo {public String load(URL url) {return url.openConnection().getContent();}}
}

我简化了在失败时重试方法调用的实际算法,但是我确定您能理解。 AOP引擎AspectJ使用@RetryOnFailure批注作为信号,通知我们必须将该类包装到另一个类中。 这是在幕后发生的。 我们没有看到实现重试算法的补充类。 但是AspectJ编织器产生的字节码包含Foo类的修改版本。

这正是这种方法的问题所在-我们看不到也不控制该补充对象的实例化。 对象组合是对象设计中最重要的过程,它隐藏在幕后的某个地方。 您可能会说,因为它是补充,所以我们不需要看它。 我不同意。 我们必须看到我们的对象是如何构成的。 我们可能不在乎它们如何工作,但是我们必须看到整个合成过程。

更好的设计如下所示(而不是注释):

Foo foo = new FooThatRetries(new Foo());

然后,执行FooThatRetries

class FooThatRetries implements Foo {private final Foo origin;FooThatRetries(Foo foo) {this.origin = foo;}public String load(URL url) {return new Retry().eval(new Retry.Algorithm<String>() {@Overridepublic String eval() {return FooThatRetries.this.load(url);}});}
}

现在,执行Retry

class Retry {public <T> T eval(Retry.Algorithm<T> algo) {while (true) {try {return algo.eval();} catch (Exception ex) {// ignore it}}}interface Algorithm<T> {T eval();}
}

代码更长吗? 是。 比较干净吗? 多很多。 我感到遗憾的是,两年前我开始使用jcabi-aspects时还不了解它。

底线是注释不好。 不要使用它们。 应该用什么代替呢? 对象组成 。

有什么会比注释更糟? 配置 。 例如,XML配置。 Spring XML配置机制是糟糕设计的完美示例。 我已经说过很多次了。 让我再重复一遍-Spring Framework是Java世界中最差的软件产品之一。 如果您可以远离它,那么您将对自己有很大帮助。

OOP中不应有任何“配置”。 如果它们是真实对象,我们将无法对其进行配置。 我们只能实例化它们。 实例化的最佳方法是运算符new 。 该运算符是OOP开发人员的关键工具。 把它从我们手中夺走并给予我们“配置机制”是不可原谅的罪行 。

  • Java注释是一个大错误(在线讲座#14); 2016年5月4日; 744意见; 13个赞
  • 依赖注入容器不是一个好主意(网络研讨会9); 2015年12月1日; 1264意见; 19个赞
  • 为何吸气与反吸是反模式? (第4场网络研讨会); 2015年7月1日; 3095次点击; 53个赞

翻译自: https://www.javacodegeeks.com/2016/11/java-annotations-big-mistake.html

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

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

相关文章

tcpdump 抓二层包_可能是我见过的最简单易懂且实用的 TCPDump 和 Wireshark 抓包及分析教程!( 强烈建议收藏 )...

公众号关注 「奇妙的 Linux 世界」设为「星标」&#xff0c;每天带你玩转 Linux &#xff01;本文将展示如何使用 tcpdump 抓包&#xff0c;以及如何用 tcpdump 和 wireshark 分析网络流量。文中的例子比较简单&#xff0c;适合作为入门参考。1. 基础环境准备为方便大家跟着上手…

JDBC分页查询及实现

当数据过多时&#xff0c;一页之内是无法显示的&#xff0c;因此需要进行分页显示。 &#xff08;一&#xff09;分页技术实现&#xff1a; 物理分页&#xff1a; - 在数据库执行查询时&#xff08;实现分页查询&#xff09;&#xff0c;查询需要的数据—-依赖数据库的SQL语句 …

SELinux深入理解

1. 简介 SELinux带给Linux的主要价值是&#xff1a;提供了一个灵活的&#xff0c;可配置的MAC机制。 Security-Enhanced Linux (SELinux)由以下两部分组成&#xff1a; 1) Kernel SELinux模块(/kernel/security/selinux) 2) 用户态工具 SELinux是一个安全体系结构&#xff0c;它…

头文件的写法

#ifndef SURF_FUNC_H_//大写&#xff0c;防止头文件被多次包含&#xff0c;防止与自带的头文件重复 #define SURF_FUNC_H_float** SURFCompare(Mat srcImage1, Mat srcImage2, float paraHessian);#endif 1.只需要将函数声明、宏定义、结构声明等内容写入。 2.头文件可以定义很…

windows调用python_如何在Windows操作系统中从R调用Python?

我正在寻找一种在Windows操作系统上调用R中的python的方法。因为似乎没有可用的R包(至少没有最近更新的包)&#xff0c;我正在寻找如何在R脚本中编写一组命令的线索&#xff0c;然后可以以批处理模式发送蟒蛇。简而言之&#xff0c;如何在Windows操作系统中从R调用python&#…

range

# i 1 # while i <100: # print(i) # i 1# range(参数) [0,参数) # for i in range(20): # range()可以被迭代 0-9 # print(i)# range(参数1, 参数2) [参数1, 参数2) 切片 # for i in range(10, 20): # print(i)# range(参数1, 参数2, 参数3) [参数1…

OSI七层网络模型与TCP/IP四层网络模型

2.1 OSI网络分层参考模型网络协议设计者不应当设计一个单一、巨大的协议来为所有形式的通信规定完整的细节&#xff0c;而应把通信问题划分成多个小问题&#xff0c;然后为每一个小问题设计一个单独的协议。这样做使得每个协议的设计、分析、时限和测试比较容易。协议划分的一个…

python小学生课本剧_二年级上学期课本剧

我们把这美丽的景色看个够。 (二人分别立在舞台两边&#xff0c;向远处眺望&#xff0c;作观赏状。) 小洁&#xff1a;(开心地)啊&#xff0c;岸上的景色真美&#xff01; 小刚&#xff1a;瞧&#xff0c;那些奇形怪状的石头真好看&#xff01; (石蛙、石鹰、石兔、石龟边舞边上…

用数组存储循环序号的数据

一个i&#xff08;i<10&#xff09;对应四个坐标值&#xff0c;需要用4*i大小的一维数组循环存储&#xff0c;或者用二维数组b[i][4]存储&#xff0c;i行4列。 一维存储方法&#xff08;类似于opencv存储彩图的顺序&#xff09;for (int i 0; i < viewMatches.size(); i…

spring 事物合并_Spring系列合并

spring 事物合并Spring Collection合并是我第一次遇到的功能&#xff0c;它是对StackOverflow 问题的回答 这是一种创建基本集合&#xff08;列表&#xff0c;集合&#xff0c;地图或属性&#xff09;并在其他Bean中修改此基本集合的方法&#xff0c;下面通过一个示例对此进行最…

2018/11/29 一个64位操作系统的设计与实现 03 (在Bochs上运行Boot程序)

使用命令dd强制将引导程序boot.bin写入引导扇区: dd ifboot.bin of/home/parallels/Documents/bochs-2.6.9/boot.img bs512 count1 convnotrunc 在P39页有完整的讲解命令的意思 注意: 书中使用的: of../../bochs-2.6.9/boot.img 是错误的. 使用命令: ./bochs -f .bochsrc 书中使…

怎么把python解释器配置在pycharm中_在PyCharm中配置项目(三):配置Python解释器...

PyCharm是一种Python IDE&#xff0c;其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。此外&#xff0c;该IDE提供了一些高级功能&#xff0c;以用于Django框架下的专业Web开发。PyCharm最新版本2019.2发布&#xff0c;功能改进提高Python语言开发效率&#…

mysql 赋给用户权限 grant all privileges on

遇到了 SQLException: access denied for localhost (using password: no) 解决办法 grant all privileges on *.* to joelocalhost identified by 1; flush privileges; 拿 joe 1 登陆 附&#xff1a; mysql> grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用…

辨析矩阵内积(hadamard、kronecker)

1. matmul product&#xff08;一般矩阵乘积&#xff09; m x p矩阵A与p x n矩阵B&#xff0c;那么称 m x n 矩阵C为矩阵A与矩阵B的一般乘积&#xff0c;记作C AB &#xff0c;其中矩阵C元素 [cij]为矩阵A、B对应两两元素之和&#xff0c;表示为&#xff1a; 例子&#xff1a…

Drools 6.5.0.Final提供

最新版本和最出色的Drools 6.5.0.Final版本现已可供下载。 这是我们先前构建的增量版本&#xff0c;重点是对6.x系列进行一些重要的改进。 您可以在此处找到更多详细信息&#xff0c;下载和文档&#xff1a; Drools网站 资料下载 文献资料 发行说明 请阅读下面的一些发行…

第一阶段总结

在这一周的冲刺周期里&#xff0c;我做了如下的事情&#xff1b; 1.在网上找到适当的图片作为软件图标&#xff1b; 2.编程出窗口的基本框架&#xff1b; 3.调整窗口的长宽高&#xff1b; 4.编写相关代码&#xff1b; 5.整合部分代码&#xff1b; 个人评分&#xff1a;99 转载于…

测试电梯的测试用例_测试用例设计经典面试题

测试用例设计经典面试题之电梯、杯子、笔、桌子、洗衣机、椅子、ATM等按测试的类型逐一测试测试类型主要分为&#xff1a;界面测试、性能测试(压力测试)、安全测试、兼容性测试几个方向。1.测试项目&#xff1a;电梯需求测试&#xff1a;查看电梯使用说明书、安全说明书等界面测…

mysql grant查看用户权限命令

本文章来给大家总结一些常用的来查看mysql用户权限命令的一些方法总结&#xff0c;在mysql中查看用户权限命令是show grants for了&#xff0c;下面我来详细介绍grants这个命令&#xff0c;有需要了解的朋友可参考。 语句 代码如下复制代码 show grants for 你的用户 比如&a…

[JSON].valueOf( keyPath )

语法&#xff1a;[JSON].valueOf( keyPath ) 返回&#xff1a;[任意类型 | null] 说明&#xff1a;获取键名路径原值&#xff0c;它保留原始值的类型 示例&#xff1a; b sysFile.binary("test.asp") 获取文件流数据 set a toJson() a.set "obj", b 将流…

任意门怎么用团发_平开门、推拉门都out了!衣柜门这样设计,实用方便又大气!...

▲ 点击蓝字“装修PLUS”&#xff0c;有关装修的问题&#xff0c;答案全在这里点击获取2020年装修精准报价冬天的时候&#xff0c;天天穿同一件外套去公司也不合适&#xff0c;但每天都换洗外套也不现实&#xff0c;所以最好的办法就是好几件外套轮流穿&#xff0c;然后换下不洗…