Java 方法命名约定:getter 太多

Posted

技术标签:

【中文标题】Java 方法命名约定:getter 太多【英文标题】:Java method naming conventions: Too many getters 【发布时间】:2011-03-13 18:35:34 【问题描述】:

为什么 Java 方法名称如此广泛地使用“get”前缀?至少在我的 Java 程序中,有很多名称以“get”开头的方法。获取方法的百分比非常高。我开始觉得“得到”这个词因为通货膨胀而失去了意义。这是我的代码中的噪音。

我注意到在函数式/声明式编程和 PL/SQL 中使用了不同的命名约定。方法名称只是说明方法返回的内容。他们将使用account.amount()Time.isoFormattedDateString(Date date) 而不是account.getAmount()Time.getIsoFormattedDateString(Date date)。这对我来说非常有意义,因为函数的名称描述了评估方法的结果(假设没有副作用,无论如何都不应该有)。 “get”前缀似乎是多余的。

我刚刚开始阅读“清洁代码”一书。它说方法应该只做一件事,而那件事通常应该是以下之一:

    通知某些对象有关事件,通常将事件作为参数传递。 询问有关某个对象的问题,通常使用形成自然语言语句的方法名称,将对象作为参数传递并返回布尔值。 获取某些内容,可能传递一些查找键或一些要转换的对象作为参数,并始终返回所需的对象/值。

我的问题是关于第三类的。这种方法除了“get”之外还有命名约定吗?您在选择方法名称/前缀时使用什么标准?

这是一个例子:

我有一个类有两个方法getDates()getSpecialDates()getDates() 只返回一个私有变量的值(对日期集合的引用)。据我了解,这是一个标准的吸气剂。 getSpecialDates() 不同;它调用getDates(),从另一个类中获取过滤器,应用过滤器并返回实际上是getDates() 的子集。

getSpecialDates() 方法可以命名为computeSpecialDates()findSpecialDates()selectSpecialDates()elicitSpecialDates() 或其他名称。或者我可以简单地将其命名为specialDates()。然后,为了保持一致性,我可以将 getDates() 重命名为 dates()

为什么要区分应该以“get”为前缀的方法和不应该以“get”为前缀的方法,为什么还要为“get”寻找替换词?

【问题讨论】:

使用“getThis”和“getThat”不会失去它的意义,只要每个get都是真正的get。它确实有助于了解您在做什么。这不像编译器会说“哦,另一个得到?现在你只是在说......” 【参考方案1】:

我个人尽可能不使用 getter 和 setter(意思是:我不使用任何需要它的框架,例如 Struts)。

我更喜欢尽可能编写 不可变 对象(public final 字段),否则我只使用公共字段:更少的样板代码,更高的生产力,更少的副作用。 get/set 最初的理由是封装(让你的对象尽可能害羞),但事实上,我并不经常需要它。

Effective Java中,Joshua Bloch 提出了这个令人信服的建议:

类应该是不可变的,除非 有一个很好的理由 它们是可变的......如果一个类不能 变得不可变,限制它的可变性 尽可能的。

在同一本书中,他还说(但我不想在这里复制整本书):

JavaBeans 模式有严重的 缺点。

我完全同意这一点,因为 JavaBeans 最初是为一个非常狭窄的问题领域设计的:在 IDE 中操作图形组件。使用为解决另一个问题而设计的解决方案是一种不好的做法。

【讨论】:

我同意。我最讨厌的事情之一:有人会说过多的公共数据是不好的,因为它使 API 变得复杂。我同意。但随后他说解决方案是将它们设为私有并创建 getter 和 setter。这有什么帮助?为什么说“x=myObject.someValue”不好但“x=myObject.getSomeValue()”消除了所有问题?我看到使用 getter 和 setter 的唯一充分理由是是否有副作用。 @Jay:当getSomeValue() 返回一个对象并且人们没有意识到现在有人可以单独使用 getter 来更改对象的状态时,情况会更糟。 我同意“首选不可变对象”,但不同意关于 JavaBeans 的最终结论。第二个引用是关于调用一堆 setter 进行构造与使用 Builder 模式 - 而不是一般地使用 JavaBeans。 JavaBeans 只是一个组件 规范。图形和 IDE 都不是必需的——BeanBox 不是 JavaBeans 的目的;这只是演示其功能的一种简单方法。如果“使用为解决另一个问题而设计的解决方案是一种不好的做法”——停止使用 Java 开发 Web 应用程序——Oak 被设计为在电视机顶盒中运行。 :P @Ardini:当然!函数名称不仅不完整而且彻头彻尾的普遍问题在于!我曾经写过一个名为“validateStocknumber”的函数,它检查股票编号是否有效并返回真或假。另一位程序员出现并更改了它以更新股票记录,但没有更改名称。然后其他人出现并对其进行了更改,因此现在它甚至不再验证库存编号!啊! 我一直认为访问器方法的动机是对值施加约束。例如,您无法阻止客户端将 public int 字段设置为负数,但您的 setter 可以。【参考方案2】:

来自JavaBeans naming conventions。

【讨论】:

如果 Java 会添加真正的属性,我们就不必再担心这个了。我对它们被从 Java 7 提案中删除感到失望。 @R. Bemrose:我同意,我希望他们能添加属性。但是,我们仍然需要担心它,所有遗留代码和现有库/框架不会消失。此外,人们通常很难改变自己的习惯。似乎很多人都使用get____,无论该方法是否可以作为属性。 库和框架不必更改,因为任何对一流属性支持的提议都可能是语法糖并在类中生成实际方法。这是必须调整的工具(比如代码生成器、IDE 等......)【参考方案3】:

有这么多 get* 方法的部分原因是 Java 不支持 .net/COM 等“属性”,Java bean 和此类使用函数 getX 和 setX 来复制名为 X 的属性的功能。一些 Java IDE 利用这一点来允许设置和检索属性。

【讨论】:

+1 Naked Objects for one 是一个依赖命名约定来生成前端的框架。 实际上(作为 Naked Objects 提交者而言),这是一个可插拔的功能。我们使用 FacetFactorys 构建元模型,默认工厂查找“get”前缀。但他们不需要。这种设计还允许我们支持其他语言;几周前我没有使用 Groovy,并且(碰巧)本周开始使用 Scala。这使用了 Scala 的 @BeanProperty 注释。但是,对于 OP,我们认为 getter 也没有什么问题:它们是一种命名约定,代表了“知道什么”的责任。 -- 丹【参考方案4】:

getter 和 setter 方法经常用 Java 编写的原因之一是因为使用了JavaBeans 约定。

然而,标准 Java API 在这方面并不一致。例如,String 类有一个length() 方法,而接口Collection 定义了一个size() 方法,而不是getLength()getSize()

Java 不支持uniform access principle,因此您必须编写 getter 和 setter 方法来访问属性。

【讨论】:

“Java 不支持统一访问原则”是什么意思? get() 方法可能返回计算值而不是私有变量的值 - 实例变量本质上与同名的 get() 或 set() 方法无关 @Tarski 你了解 UAP 吗?这意味着您可以像访问成员变量一样访问属性。所以像Foo.thingy 这样的东西真的会在幕后调用Foo.getThingy()Foo.thingy 看起来好像您正在直接访问成员 var,但实际上并非如此。 C#、Ruby、Scala 等语言都支持这一点。 JavaBeans 约定要求每个字段都有 get/set 方法,但 lenghtsize (很可能)不是字段。实际上,它们似乎是基于字段的计算值 它也可能是对物体的一般感知。假设您有一个房间作为对象。房间里有一台电视机。然后,您将编写 getTelevision() 以获取有关房间中某些东西的信息,并编写 squareFoot() 以获取房间的大小(属性)。但是 getSquareFoot() 将用于识别容易从字段返回值的方法。【参考方案5】:

getSpecialDates()computeSpecialDates()findSpecialDates()selectSpecialDates()elicitSpecialDates() 这样的方法名称对我来说是命令,因为在它们的名称中使用了动词(动作)。每次调用命令时,命令都会产生副作用。而像date()dates()specialDates() [名词] 这样的方法名称是返回有用值且没有副作用的方法。多次调用该方法每次都返回相同的值,除非调用了一个副作用是改变状态的命令。

【讨论】:

【参考方案6】:

一个原因是它是Java Bean Spec 的重要组成部分。

【讨论】:

【参考方案7】:

要求 Java 开发人员使用通用 get/set 约定的原因之一是许多框架依赖它来创建 bean 和设置字段。例如,如果您为 Spring bean 配置了一些属性,例如 <property name="foo" value="bar" />,并且类中没有名为 setFoo() 的方法,则创建 bean 时会出错。

【讨论】:

【参考方案8】:

前提1:一个方法应该只做一件事。前提 2:getter 方法——不管它是否使用 get 前缀——应该没有副作用。考虑到这两个前提,我提出:一种方法,其作用是获取某些东西,并且以相对简单且廉价的方式进行,其名称中不需要有动词。

getter 的存在理由不是做某事,而是评估某事。我们对方法做什么不感兴趣。由于它没有副作用,因此该方法中进行的任何计算都不会引起任何兴趣。我们只对方法返回感兴趣。方法名称应以名词的形式反映。仅由名词组成的方法名称应始终为“getters”。

前缀“get”中的信息可以通过缺少动词来推断。这比使用 get 前缀更简单、更直观。

可以假定名称仅由名词组成并具有返回值的方法没有副作用并且相对便宜。名称包含动词且没有返回值的方法存在具有副作用。名称中包含动词并有返回值的方法可以被认为是相对昂贵的并且可能有副作用。

似乎每个人都在到处写“get”的原因仅仅是源自 JavaBeans 模式的教条传统。当您真正计划使用需要它的工具/框架时,请保留 get 前缀!

【讨论】:

【参考方案9】:

就个人而言,我沉迷于get。这只是人类的语言。当你想要某样东西时,你想要get某样东西。 get 前缀没有任何问题。关于命名约定,我可以想到数据库查询的 Select 前缀——例如 SelectUsers

【讨论】:

正如我在问题中所说的,get 前缀似乎有问题的是它通常看起来没有必要,因此只是噪音,因此应该被省略。 这只是你的意见,不是事实。 +1 双手 :) 如果您的类中有 100 个方法(或通过层次结构可用),那么只需键入“yourObjectInstance.get”并稍等一下,强制自动完成IDE 过滤所有“get”方法。如果他们没有从 get 开始,那么很难找到合适的。这种混淆的典型例子是 collection.size() 和 array.length()【参考方案10】:

正如许多人已经说过的,get..() 和 set()... 是 Java Beans Convention 的一部分。这对于与 Java 规范的其他部分的互操作是必需的。例如,在 JSP 中,您可以通过指定不带 get 前缀的属性名称来访问 Java 中的成员。

给定 bean:-

public class Foo 
  public int getX()  return 1; 

我们可以通过下面的JSP来获取X:-

<jsp:useBean id="aFoo" class="Foo" />
<c:out value="$aFoo.X" />

这种方法除了“get”之外还有命名约定吗?

是的,您可以将is 代替get 用于布尔属性。

【讨论】:

【参考方案11】:

当我们生活在这样一个时代,任何值得拥有的 IDE 都会为您的私有变量生成 getter 和 setter 并让您在不想阅读它们时将它们折叠起来,那么“get”有什么意义?

你真正的问题应该是关于设计:为什么你的对象有这么多属性?如果您的对象只有 getter 和 setter,那么您是否患有“贫血的领域模型”?

C# get, set 表示法稍微好一点,因为它减少了代码行数,但您仍然需要为每个变量键入那个讨厌的“get”。

【讨论】:

【参考方案12】:

正如其他人所提到的,它适用于 Java Beans。但是,如果您使用的是 Java,请仅命名一个方法 getXXX(),前提是它只返回一个值并且不执行任何其他操作。就像你暗示的那样,如果它正在做其他事情,请将其命名为不同的名称,例如 computeXXX()。

我有时会发现 getXXX() 方法有 50 行代码 - 如果是这种情况,那么你做错了。

【讨论】:

【参考方案13】:

我开始觉得这个词 “get”正在失去意义,因为 的通货膨胀。这是我的代码中的噪音。

我有点不同意这个结论。我不会说它失去了意义,我会说,由于它被广泛使用,带有 get 前缀的方法几乎可以完成您期望它们做的事情。

以下示例:

Time.isoFormattedDateString(Date date)

这是否根据输入参数设置格式类型,以便所有后续调用都使用此格式?

我知道有人会得出这样的结论有点牵强,因为它是一个静态方法,但是您确定是否在实例上调用了此方法?可能,但使用 get 消除了所有歧义:

getIsoFormattedDateString(Date date)

在我看来,属性是一个比完全删除 get 更优雅的解决方案。

【讨论】:

@donroby 是的,在某种程度上我同意这个例子。但是,如果您完全脱离上下文查看方法名称,您会知道它做了什么吗?当它立即向任何开发人员指示该方法将做什么时,为什么要删除“get”前缀。这应该是命名方法时的关键问题。 在这种情况下,“get”意味着我读取了一个属性。如果我的方法仅对输入参数进行操作以产生输出值,那么我不会将其称为“getter”。我会做Time.toISO8601(Date) 还是ISO8601.from(Date)?当然,为我们完成这项工作的 Java 8 的 Instant.toString() 完全弃用了 OP 问题的这一特殊特征。【参考方案14】:

历史 sn-p:如果您查看一些最早的 Java 1.0 API(JavaBeans 之前),您会发现它们没有“get”前缀。例如 java.awt.Container#minimumSize() 已被#getMinimumSize() 取代。

【讨论】:

【参考方案15】:

我认为这是“给你的变量和函数起有意义的名字”理想的一个子集。

正如许多人所指出的,“get”在 Java Bean 中具有特定的含义。因此,我认为它应该仅限于用于检索内部变量的值,可能会产生副作用。我认为如果“获取”涉及较小的计算,例如进行数据类型转换或从嵌入式类中提取值或重新解释其他值,例如“public int getRightMargin() return width-margin.left; ”,这是可以接受的。任何副作用都应仅限于获取值的真正“副作用”,例如设置一个表示已检索到它的标志。

但如果有严肃的计算,我认为它不应该被称为“get”。也许是“计算”或其他什么。

如果我们在命名函数时使用一致的术语会很好,比如如果我们都同意“读取”意味着主要活动是从数据库中检索某些内容,而“计算”意味着进行计算或类似的事情.但这可能是不现实的:也许有太多的案例有细微的差别。

【讨论】:

【参考方案16】:

一种选择是为返回原始值或不可变值的方法保留 get 前缀,但为返回可用于修改原始接收者的引用的方法删除前缀。

例如在java.util.Map 中,size() 可以称为getSize(),但keySet()不会称为getKeySet()

【讨论】:

【参考方案17】:

我仅将 get 和 set 用于仅获取或设置属性而不是其他方法的方法。

【讨论】:

如果没有get*,其他方法你用什么?【参考方案18】:

Java Beans 非常坚持其命名约定,例如 假设你声明了一个变量名 Name,对应的 setter 为 setName()。但它会产生一个错误,因为 setName 必须对应于 'name' 而不是 Name。 另一个例子 boolean isReadey;使用吸气剂 isReady()。再次出错,因为它正在寻找布尔就绪。 因此,在编写代码之前,您必须熟悉此命名约定。 但我个人更喜欢这种约定,因为它使程序员的工作变得容易,并且在你使用它几分钟后似乎有点合乎逻辑。

【讨论】:

【参考方案19】:

好吧,尽管 JavaBeans 规范要求您声明 getter 和 setter,但我通常不会声明它们,除非绝对必要(如许多 MVC 框架的情况)。我在 Java 职业生涯中做了很多工作,我倾向于将变量声明为 public(是的,这听起来有点非 OOPy)。但我喜欢它,因为它看起来简洁而且“我”知道自己在做什么。它的唯一优点是行数减少。

【讨论】:

【参考方案20】:

保留“get”前缀很重要,因为:

一个方法应该声明一个动作,因此它的名字中必须包含一个动词

get 表明变量的状态不会改变

在这个表达式中,您如何区分方法account() 和变量account

newAccount = currentAccount + account() --- 这个account() 做什么?


您在代码中看到太多 getter 的原因应该让您担心!

要么将班级分成较小的班级,要么 只需更好地隐藏您的代码,因为您不必透露您的类实习生,甚至应该尽可能地隐藏它们!

【讨论】:

我不明白为什么“getter”必须声明一个动作:它不做任何事情,它评估某些东西。为了表明状态不会改变,你可以在方法名中去掉动词。如果我看到一个名为 account() 的方法,我会假设它返回一个代表帐户的任何类型的对象,比如一个名为 Account 的接口。如果我看到一个名为 amount() 的方法,我会假设它返回一个表示金额的数字。谁不会? 我说方法名前面的 GET 字不应该引起这样的骚动!而且我不会使用 account() 因为那会使我的程序更加混乱和模棱两可。我不想在我的程序中为那些在我之后阅读它的人留下歧义。我个人很容易将 ACCOUNT() 误读为 ACCOUNT 并且我认为这不是一种方法......如果您决定放弃一次代码清晰度,您稍后会再做一次。我认为代码的清晰性比缩短 3 个字符的方法更重要...... 如果你读得更好,我写过“一个方法应该声明一个动作”,因为你争取封装,你使用方法和方法应该声明一个动作,因为这是它们与变量或类型的不同之处(类)......再次 - 代码清晰。但是还有另一个立场 - 制定自己的代码编写约定并坚持下去 - 保持一致比“以大写还是小写开头的变量更好”更重要...... 感谢您的意见,但我认为您错过了我的观点。一方面没有必要区分变量,另一方面没有副作用的方法会返回一些东西。它们的语义相同,因为它们表示一个值。从语法上讲,它们必须通过方法名称末尾的圆括号 () 有所不同。如此小的句法差异足以暗示 无趣 的事实,即尽管语义相同,但属性的技术实现是方法而不是变量。

以上是关于Java 方法命名约定:getter 太多的主要内容,如果未能解决你的问题,请参考以下文章

返回布尔值的 Java 方法的命名约定

Typescript 类受保护字段命名约定

可选绑定的命名约定

C++,Java编程中 标识符 常见命名约定

如何验证XML架构中的命名约定

TypeScript 中类属性的命名约定