注释是在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

Java注释是一个大错误相关推荐

  1. java实现把一个大文件切割成N个固定大小的文件

    这个好像是我一年前去面试时的一道面试题,分享一下!考 java I/O 的! //java实现把一个大文件切割成N个固定大小的文件 package com.johnny.test; import ja ...

  2. centos 的网关和什么相同_CentOS操作系统:为什么转移到CentOS流是一个大错误

    就我个人来说,我一直用的deepin的,但我们管理的服务器上全部使用的都是CentOS. 如果你足够关注开源,你可能已经听到了忠实用户的最新抱怨--Red Hat已经宣布它正在杀死我们目前所知道的Ce ...

  3. java代码输出一个大的比心手势图形

    你可以使用字符画来输出一个大的比心手势图形. 下面是一个简单的代码实现: public class Heart {public static void main(String[] args) {Sys ...

  4. 转:学习为了什么?我一直说学习是为了学会更好的思考,其实更通俗的讲学习是为了避免犯大错误...

    ㊣华哥日记㊣ 3.22 学习为了什么?我一直说学习是为了学会更好的思考,其实更通俗的讲学习是为了避免犯大错误,兄弟姐妹们,你们知道一个大错误可以让我们浪费几年甚至十几年吗,人生承受不住大错误,可我们大 ...

  5. 苹果库克任职首年需避免十大错误:授权iOS

    北京时间2月1日消息,据国外媒体报道,美国知名IT杂志<eWeek>网络版今天撰文,称苹果新首席执行官蒂姆·库克(Tim Cook)执政后还远远不足一年时间,但在他担任此职的第一年时内,必 ...

  6. Java黑皮书课后题第5章:*5.23(演示抵消错误)当处理一个很大的数字或很小的数字时候,会产生一个抵消错误。……编写程序对上面的数列从左到右和从右向左计算的结果进行比较,n=50000

    5.23(演示抵消错误)1 + 1/2 + 1/3 + -- + 1/n,编写程序对上面的数列从左到右和从右向左计算的结果进行比较,n=50000 题目 题目概述 破题 代码 运行示例 题目 题目概述 ...

  7. java contains_Java开发人员犯的十大错误,你犯几个?

    本篇为译文: 这个列表总结了Java开发人员经常犯的十大错误. 1.将Array转换为ArrayList 若要将数组转换为ArrayList开发人员经常这样做: List<String> ...

  8. java构造函数_Java开发人员也会犯的十大错误

    今天我就给大家总结一下Java开发人员经常犯的10大错误. #1 将数组转换为ArrayList 要将数组转换为ArrayList,开发人员通常会这样做: List < String > ...

  9. Java开发人员经常犯的10大错误

    我们在这里总结了Java开发人员经常犯的十大错误,看看你中了几个? 1.将Array转换为ArrayList 要将Array转换为 ArrayList,开发人员通常会这样做: List<Stri ...

最新文章

  1. 求解稀疏优化问题——增广拉格朗日方法+半光滑牛顿法
  2. OSChina 周五乱弹 —— 我觉得相亲是耻辱
  3. 7 centos 源码安装samba_Centos-7安装zabbix
  4. vue Cli 脚手架的搭建
  5. JSONObject toJSONString错误
  6. Python中MRO
  7. Ajax学习总结(2)——Ajax参数详解及使用场景介绍
  8. 推荐一个不错的plist拆解工具,untp
  9. 网关支付、银联代扣通道、快捷支付、银行卡支付分别是怎么样进行支付的?...
  10. 易语言html代码解释器,易语言执行javascript
  11. 华硕xhci灰色_[安装实录] 零基础完美黑苹果安装之华硕篇
  12. 怎么更改wifi频段_wifi信道和频段怎么设置?
  13. 教你下载微信小程序里的视频
  14. 10M,50M,100M宽带下载速率解惑之网络基础知识
  15. python数据类型(下)
  16. The Devil Wears Prada-10
  17. 基于ssm的校园二手物品交换系统
  18. 2021年高压电工考试报名及高压电工最新解析
  19. 名词解释第八讲:公钥
  20. [ aardio笔记 ] (一)基本使用、调用python与下拉框

热门文章

  1. Maven精选系列--eclipse各种操作
  2. Spring Boot 发布 jar 包转为 war 包秘籍。
  3. 这本书强烈推荐看看!
  4. 第一章数据库系统基础
  5. Servlet使用适配器模式进行增删改查案例(EmpServiceImpl.java)
  6. RBAC(基于角色的权限访问控制)
  7. 一文让你秒懂各种进制的前缀
  8. Mysql对字符串去掉前后空格(trim)或者指定字符
  9. qt弹簧教程_弹簧启动执行器教程
  10. java登录界面命令_Java命令行界面(第11部分):CmdLn