Java Annotations Are a Big Mistake

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

注解在Java 5中被引入,我们都感到非常兴奋。这是一个很棒的工具,可以使代码更短!不再需要Hibernate/Spring的XML配置文件!就在我们需要的代码中,只需要注解。不再需要标记接口,只需要一个运行时保留通过反射发现的注解!我也很兴奋。此外,我开发了一些大量使用注解的开源库,比如jcabi-aspects。然而,我现在已经不再兴奋了。而且,我认为注解是Java设计中的一个大错误。

长话短说,注解有一个大问题——它们鼓励我们在对象之外实现对象功能,这与封装的原则背道而驰。对象不再是完整的,因为它的行为不完全由自己的方法定义——其中一部分功能留在其他地方。为什么这是不好的呢?让我们通过几个例子来看。

假设我们使用@Inject注解一个属性:

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

现在我们通过容器创建了Books类的一个实例。

Books 类不知道如何以及谁会将 DB 类的实例注入到它中。这将在幕后和它无法控制的地方发生。注入会完成这一任务。这种方式可能看起来很方便,但这种态度会对整个代码库造成很大的破坏。控制失去了(不是反转,而是失去了!)。对象不再负责任。它无法对发生的事情负责。

相反,下面是正确的做法:

本文解释了为什么依赖注入容器从一开始就是一个错误的想法:依赖注入容器是代码污染者。注释基本上促使我们创建和使用容器。我们将功能从对象中移出并放入容器或其他地方。这是因为我们不想重复编写相同的代码,对吗?没错,重复是不好的,但拆散一个对象甚至更糟糕。更糟糕的是,ORM(JPA/Hibernate)也是如此,其中主动使用了注释。请查看这篇文章,它解释了ORM的问题:ORM是一种冒犯性的反模式。注释本身并不是主要的动机因素,但它们通过拆散对象并将其部分放在不同的位置上来帮助和鼓励我们。它们是容器、会话、管理器、控制器等。

这是 JAXB 的工作原理,当你想要将你的 POJO 转换为 XML 时。首先,你需要将 @XmlElement 注解附加到 getter 方法上。

然后,您创建一个编组程序并要求它将类Book的一个实例转换为XML。

谁在创建XML呢?不是book。是Book类之外的其他人。这是非常错误的。相反,应该这样做。首先,那个对XML一无所知的类:

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

现在,为了将书籍打印成XML格式,我们需要执行以下步骤:

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

以下是一个示例(来自我的自有库):

编译完成后,我们运行一个所谓的AOP织入器,技术上将我们的代码转换为类似以下的形式:

我简化了在方法调用失败时重试的实际算法,但我相信你能理解。AspectJ 是一个 AOP 引擎,使用 @RetryOnFailure 注解作为信号,告诉我们该类必须被包装到另一个类中。这是在后台发生的。我们看不到那个实现重试算法的辅助类。但是,AspectJ 织入器生成的字节码包含了类 Foo 的修改版本。

这就是这种方法的问题所在——我们看不到并且无法控制辅助对象的实例化。对象组合是对象设计中最重要的过程,但它被隐藏在后台某个地方。你可能会说,我们不需要看到它,因为它是辅助的。我不同意。我们必须看到我们的对象是如何组合的。我们可能不关心它们的工作原理,但我们必须看到整个组合过程。

一个更好的设计应该是这样的(而不是使用注解):

然后,FooThatRetries 的实现:

现在,Retry的实现如下:

代码变长了吗?是的。更加简洁了吗?更加简洁了很多。我很后悔两年前开始使用jcabi-aspects时没有理解它。

底线是注解是不好的。不要使用它们。那么应该使用什么呢?对象组合。

还有比注解更糟糕的东西吗?配置。例如,XML 配置。Spring XML 配置机制是一个糟糕设计的典型例子。我之前已经说过很多次了。让我再重复一遍——Spring Framework 是 Java 世界中最糟糕的软件产品之一。如果你能远离它,你将会给自己带来很大的好处。

面向对象编程中不应该有任何 “配置”。如果我们的对象是真实的对象,就不能对它们进行配置。我们只能实例化它们。而最佳的实例化方法是运算符 new。这个运算符是面向对象编程开发者的关键工具。把它从我们手中拿走并给予我们 “配置机制” 是一种不可饶恕的罪行。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-05 at 21:33

sixnines availability badge   GitHub stars