反对注释的论据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反对注释的论据相关的知识,希望对你有一定的参考价值。

我的团队正在转向Spring 3.0,有些人想要开始将所有内容都移到Annotations中。当我看到一个类似这样的方法的类时,我的肠道感觉非常糟糕(代码味道?):(只是一个例子 - 不是所有真正的注释)

@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}

我只是落后于时代,还是这对所有人来说都是一个可怕的想法?而不是使用诸如继承和多态的OO概念,现在通过约定或通过注释来实现一切。我只是不喜欢它。必须重新编译所有代码来改变IMO配置的东西似乎是错误的。但它似乎是一切(特别是春天)的方式。我应该“克服它”还是应该推回并尝试尽可能地保留我们的代码作为注释?

答案

实际上我认为你的直觉中的不良感觉更多地与注释混合配置与代码有关。

我个人感觉和你一样,我宁愿在代码库本身之外和外部Spring XML上下文文件中保留配置(例如事务定义,路径元素,控制器应该映射到的URL等等)。 。

我认为虽然这里的正确方法归结为意见以及您更喜欢哪种方法 - 但我预测社区的一半会同意注释方法而另一半会同意外部配置方法。

另一答案

注释通常会引入不属于此类依赖项的依赖项。

我有一个类巧合,它具有类似于RDBMS模式中表的属性的属性。该类是在考虑此映射的情况下创建的。类和表之间显然存在关系,但我很高兴保持该类免于声明该关系的任何元数据。这个类在完全不同的系统中引用表及其列是否正确?我当然不反对将两者联系起来的外部元数据,并使每个元数据都无法理解另一个。我获得了什么?并不是说源代码中的元数据提供了类型安全性或映射一致性。任何可以分析JPA注释的验证工具都可以很好地分析hibernate映射文件。注释没有帮助。

在一份合同中,我创建了一个maven模块,其中包含来自现有包的接口实现包。不幸的是,这个新软件包是单片构建中​​的众多目录之一;我把它视为与其他代码分开的东西。尽管如此,该团队正在使用类路径扫描,因此我必须使用注释才能将我的组件连接到系统中。在这里,我不希望集中配置;我只想要外部配置。 XML配置并不完美,因为它将依赖性布线与组件实例化混为一谈。鉴于Rod Johnson不相信基于组件的开发,这是公平的。尽管如此,我再次感到注释并没有帮助我。

让我们将这与我不感兴趣的事情进行对比:TestNG和JUnit测试。我在这里使用注释,因为我知道我正在使用TestNG或JUnit来编写此测试。如果我为另一个替换一个,我理解我将不得不执行一个代价高昂的转换,这个转换会偏离重写测试。

无论出于何种原因,我接受TestNG,JUnit,QUnit,unittest和NUnit拥有我的测试类。在任何情况下,JPA或Hibernate都不会拥有碰巧映射到表的那些域类。在任何情况下,Spring都不会拥有我的服务。我控制我的逻辑和物理包装,以隔离依赖于其中任何一个的单元。我想确保远离一个人不会因为它留下的所有依赖性而使我瘫痪。说再见总比离开容易。在某些时候,离开是必要的。

另一答案

它是2018年,这一点仍然相关。

我注释的最大问题是你不知道注释在做什么。你正在削减一些代码并将其隐藏在其他地方。

引入了注释以使语言更具说明性并且更少编程。但是,如果您将大部分功能转移到注释,那么您实际上是将代码切换为另一种语言(而不是非常好的语言)。编译时检查非常少。这篇文章提出了同样的观点:https://blog.softwaremill.com/the-case-against-annotations-4b2fb170ed67

整个启发式“将一切都移到配置中,以便人们不必学习如何编码”已经失去控制。工程经理没有思考。

例外:

  • JUnit的
  • JAX-RS
另一答案

根据我的经验,注释很简单:

  • 无法在注释中强制执行类型安全
  • 序列化问题
  • 交叉编译(例如javascript)可能是个问题。
  • 需要注释的库/框架从外部库中排除未注释的类。
  • 不可覆盖或可互换
  • 您的项目最终会强烈依赖于需要注释的系统

如果Java有类似“方法文字”的东西,你可以在相应的注释类中注释一个类。如下所示:例如javax.persistence和以下带注释的类:

@Entity
class Person
{
    @Column
    private String firstname;
    public String getFirstname() { return firstname; }
    public void setFirstname(String value) { firstname = value; }

    @Column
    private String surname;
    public String getSurname() { return surname; }
    public void setSurname(String value) { surname = value; }

}

而不是注释,我建议一个映射类,如:

class PersonEntity extends Entity<Person> {
    @Override
    public Class<Person> getEntityClass() { return Person.class;}

    @Override
    public Collection<PersistentProperty> getPersistentProperties() {
         LinkedList<PersistentProperty> result = new LinkedList<>();
         result.add(new PersistentProperty<Person>(Person#getFirstname, Person#setFirstname);
         result.add(new PersistentProperty<Person>(Person#getSurname, Person#setSurname);
         return result;
    }
}

此伪Java代码中的虚构“#”符号表示方法文字,当在给定类的实例上调用时,它会调用该实例的相应委托(自java以来​​用“::”签名)。 “PersistentProperty”类应该能够强制方法文字引用给定的泛型参数,在本例中为Person类。

通过这种方式,您可以获得比注释更多的好处(比如对“annotate”类进行子类化)并且您没有上述缺点。您也可以拥有更多针对特定领域的方法。唯一的预注释就是通过注释,您可以快速查看是否忘记包含属性/方法。但是,通过Java中更好的元数据支持,这也可以更简洁,更正确地处理(例如在Protocolbuffers中考虑像需要/可选的东西)

另一答案

也许你有一个遍及代码的冗余注释问题。使用meta-annotations可以替换冗余注释,并且您的注释至少是DRY。

来自Spring博客:

@Service
@Scope("request")
@Transactional(rollbackFor=Exception.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
}

@MyService
public class RewardsService {
…
}

由于Java发展得如此缓慢,人们会将更多语言中缺少的功能放入注释中。这是一件好事,Java可以以某种形式进行扩展,这是一件坏事,因为大多数注释都是一些解决方法并增加了复杂性。

另一答案

我最初也对注释持怀疑态度,但看到它们在使用中,它们可能是一件好事。它们也可能被过度使用。

关于注释要记住的主要事情是它们是静态的。它们无法在运行时更改。任何其他配置方法(xml,代码中的自我描述,无论如何)都不会受此影响。我已经看到这里的人在Spring上遇到了关于注入测试配置的测试环境以及必须下载到XML以完成它的问题。

XML不是多态的,继承的或其他任何东西,因此在这个意义上它不是倒退。

注释的优点是它可以为您的配置提供更多静态检查,并且可以避免XML配置中的大量冗长和协调困难(基本上保持DRY)。

就像XML一样,Annotations可能会被过度使用。重点是平衡每个人的需求和优势。注释,在某种程度上,它们为您提供了较少的详细和DRYer代码,是一种可以利用的工具。

编辑:关于替换接口或抽象类的注释的注释,我认为在框架边界可能是合理的。在一个旨在被数百个(如果不是数千个)项目使用的框架中,具有接口或基类可以真正地压缩事物(特别是基类,尽管如果你可以用注释来做,那么你没有理由不做它具有常规界面。

考虑JUnit4。之前,您必须扩展具有设置和拆除方法的基类。对于我的观点,如果它们是在接口上或基类中并不重要。现在我有一个完全独立的项目,它有自己的继承层次结构,他们都必须遵守这个方法。首先,他们不能拥有自己的冲突方法名称(在测试框架中没什么大不了的,但是你明白我的观点)。其次,你必须完全调用超级链,因为所有方法都必须耦合。

现在使用JUnit4,您可以在层次结构中的不同类中使用不同的@Before方法,它们可以彼此独立。没有注释,没有同样的DRY方法来实现这一点。

从JUnit的开发人员的角度来看,这是一场灾难。拥有一个可以调用setUp和teardown的已定义类型会好得多。但是为了方便框架开发人员而不存在框架,它为了方便框架用户而存在。

如果您的代码不需要关心类型,那么所有这些都适用(也就是说,在您的示例中,无论如何都不会真正使用Controller类型)。然后你甚至可以说实现框架的界面比放置注释更漏洞。

但是,如果您要编写代码来读取您自己项目中的注释,请远程运行。

另一答案

我个人觉得注释占用了太多,并且已经从它们的原始和超级有用的目的(例如,指示被覆盖的方法之类的小事)中被炸成了这个疯狂的元编程工具。我不认为JAva机制足够强大,可以处理每种方法之前的这些注释集群。例如,我现在正在与JUnit注释作斗争,因为它们以我不喜欢的方式限制我

话虽这么说,根据我的经验,基于XML的配置也不是很好。所以引用南方公园,你要选择一个巨大的冲洗和一个t * rd三明治。

我认为您必须做出的主要决定是,您是否更熟悉弹簧配置的非本地化(即,维护两个文件而不是一个),以及您是否使用受益于注释的工具或IDE插件。另一个重要问题是使用或维护代码的开发人员是否真正理解注释。

另一答案

检查这些类似问题的答案

What are the Pros/Cons of Annotations (non-compiler) compared to xml config files

Xml configuration versus Annotation based configuration

基本上它归结为:使用两者。他们两个都有用例。不要将注释用于那些应该保持可配置而不重新编译所有内容的东西(特别是你的用户应该能够配置的东西,而不需要你重新编译所有东西)

另一答案

我认为如果将它们与度量一起使用,注释就会很好。像@WebService这样的注释在部署和运行时做了很多工作,但它们不会干扰类。 @Cachexxx或@Transactional通过创建代理和大量工件明显干扰,但我认为它们已受到控制。

当使用带有注释和CDI的Hibernate或JPA时,事情开始变得混乱。注释增长很多。

IMO @Service和@Repository是应用程序代码中Spring的干扰。它们使您的应用程序依赖于Spring,仅供Spring使用。

Spring Data Graph的案例是另一个故事。例如,@ NodeEntity在构建时向类添加方法以保存域对象。除非你有Eclipse和Spring插件,否则你会出错,因为源代码中不存在这些方法。

对象附近的配置有其好处,但也有一个配置点。注释在测量方面很好,但它们并不适用于所有内容,并且当存在与源代码行一样多的注释行时,它们确实很糟糕。

我认为春天的道路是错的;主要是因为在某些情况下没有其他方法可以做这些有趣的事情。就像Spring想要进行xtreme编码一样,同时他们将开发人员锁定在Spring框架中。可能Java语言需要另一种方法来做一些事情。

另一答案

必须谨慎使用注释。他们对一些人有益,但对所有人都不是。至少xml配置方法将配置保存在一个文件(或多个)中,而不是遍布整个地方。这将引入(我喜欢称之为)糟糕的代码组织。如果配置分布在数百个文件中,您将永远不会看到配置的完整图片。

另一答案

我认为这在某种程度上取决于你何时开始编程。我个人认为他们很可怕。主要是因为他们有一些准“意义”,你不会理解,除非你碰巧知道有问题的注释。因此,它们本身就形成了一种新的编程语言,使您远离POJO。与(比如)普通的旧OO代码相比。第二个原因 - 它们可以阻止编译器为您完成工作。如果我有一个庞大的代码库并且想要重构某些东西或者重命名某些东西,那么理想情况下我喜欢编译器抛出所有需要改变的东西,或者尽可能地。注释应该是那样的。注释。不是代码行为的核心。它们最初设计为在编译时可选地省略,它告诉您需要知道的所有内容。

是的,我知道XML配置会以同样的方式受到影响。这不会让情况变得更糟,同样糟糕。至少我可以假装忽略它 - 它并没有在每一个方法或参数声明中盯着我。

鉴于我的选择,我实际上更喜欢可怕的旧J2EE远程/家庭界面等(最初受到Spring人员的批评)至少可以让我了解最新情况,而无需研究@CoolAidFrameworkThingy及其缺陷。框架人员的一个问题是,他们需要将您与他们的框架联系起来,以使整个企业在财务上可行。这与设计框架很不一致(即,它可以作为独立的,可以从代码中移除)。

不幸的是,注释很时髦。所以你很难阻止你的团队使用它们,除非你进入代码审查/标准等(也不合时宜!)

我读到Stroustup从C ++中留下了注释,因为他担心这些注释会被误用。有时事情往往走错了几十年,但你可以希望事情能够及时完成。

另一答案

像许多事情一样,有利有弊。在我看来,一些注释是好的,虽然有时感觉有一种过度使用注释的倾向,当一个普通的旧函数调用方法可能是优越的,并且作为一个整体,这可能无意中增加认知负荷,因为它们增加了如何“做事”。

让我解释。例如,我很高兴您提到了@Transactional注释。大多数Spring开发人员可能会了解并使用@Transactional。但是,有多少开发人员知道@Transactional实际上是如何运作的?他们是否会在不使用@Transactional注释的情况下知道如何创建和管理事务?使用@Transactional可以让我在大多数情况下更容易使用事务,但在特殊情况下,当我需要对事务进行更细粒度的控制时,它会隐藏这些细节。所以在某种程度上它是一把双刃剑。

另一个例子是Spring配置类中的@Profile。在一般情况下,它可以更容易地指定您希望加载Spring组件的配置文件。但是,如果您需要比仅指定要加载组件的配置文件列表更强大的逻辑,则必须获得环境对象自己并编写一个函数来执行此操作。同样,大多数Spring开发人员可能熟悉@Profile,但其副作用是他们对其工作原理的细节不太熟悉,例如Environment.acceptsProfiles(String ... profiles)函数。

最后,当注释不起作用时,可能更难理解为什么,你不能只在注释上放置一个断点。 (例如,如果您忘记了配置上的@EnableTransactionManagement,会发生什么?)您必须找到注释处理器并对其

以上是关于反对注释的论据的主要内容,如果未能解决你的问题,请参考以下文章

vbscript 各种自定义代码片段 - 有关详细信息,请参阅注释

Android P 中不推荐使用的片段

DTO中的继承和自定义逻辑[关闭]

vscode 用户代码片段 vue初始化模板 Snippet #新加入开头注释 自动生成文件名 开发日期时间等内容

什么是“论据”?

phpstorm 实用快捷键 和 注释