接口设计-访问方法是否有命名约定?

7

关于 Java 接口设计中访问方法的命名问题,我有一个疑问。通常情况下,即使我没有设置方法,我也经常在接口中使用 "get" 前缀来表示访问方法。

例如:

public MyObject getTask(int id, String version);

我考虑了很多重构,也会通过将方法重命名来影响我的接口

public MyObject findTask(int id, String version);

我的问题是:是否有合理的规则来决定接口方法应该设计为查找器(finder)还是获取器(getter)方法?或者这是一些相当随意的事情?


你在“getting”和“finding”之间做了什么区分? - Basil Bourque
通常情况下,如果根据提供的“id”和“version”未找到“MyObject”,当“findTask”仅返回“null”时,“getTask”将引发异常。 - Dmitry Bychenko
当没有找到结果时,返回“null”还是抛出异常的决定似乎是一个有用的标准 - 谢谢。 - Ralph
3个回答

2

getXYZ 方法通常传达的是实例返回的信息很容易被访问。更多时候,这些方法只是返回[可能是只读副本]数据成员或对它们进行非常简单的操作。这种概念驱动的行为是你不需要过度思考使用该方法 - 调用它是便宜的,并且不值得再考虑。

findXYZ 方法传达的是尽管实例能够检索此信息,但它并不是立即可用的 - 它需要去查找它 - 例如,通过查询数据库或服务目录。这种概念驱动着在调用这样的方法之前三思而后行,并努力保存或缓存其结果的行为。


这是一个很好的观点!但是我认为当我调用访问方法时,我从来没有担心过内部实现细节;-) - Ralph
特别是你的提示“非常简单的操作”很到位。虽然getter方法不应该包含逻辑,但这并不意味着它们不能包含Collections.unmodifiable...或Optional.ofNullable-wrappers。 - Jan B.

2

“getter”是指直接返回属性值。您还可以了解属性的读取方式,包括字段访问和属性访问。

这在某种程度上是从JavaBeans继承下来的。

查找器意味着您有一个搜索感兴趣的值的标准。

示例实现:

public class User{
  private List<Category> categories;

   public void getCategories() {
      return categories;
   }

   public void findCategory(String categoryName) {
       // your logic here
   }

   public void findCategory(Predicate predicate)  {
        // your logic here
   }

}

同时在接口中定义getter方法可能不是一个好主意,应该相反。应该将查找方法放在接口中。
但这取决于具体情况。我只能想到一种情况需要在接口中定义getter方法,那就是如果你遵循JavaBeans原则并且同时想支持多个bean的实现。但我本身不太喜欢这种方式。
在此链接中,您可以找到一些有关何时应在接口中存在getter方法的有趣材料。 Java Interface Usage Guidelines -- Are getters and setters in an interface bad? 流畅API
我还建议您阅读有关什么是流畅API的文章。这是在@Basil提供的java.time包示例的上下文中。 https://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/

一个带有参数的方法根据Java Beans的定义,即使方法名称以getis开头,也不是“getter”方法。 - Andreas
问题的作者在标题中明确使用了“getter”。他在问题主体中用连字符第二次使用了getter。 - Alexander Petrov

2

tl;dr

前缀:

  • get…set…
    用于访问对象的简单状态,没有副作用或复杂性。
  • fetch…submit
    当访问对象涉及大量工作量、副作用或可能出现异常/空值时使用。

没有严格规定

没有关于命名这些方法的严格规则。

JavaBeans

“get”和“set”方法通常在Java中被广泛使用,符合JavaBeans规范传统。通常用于访问对象的简单状态,或该状态的计算/格式化形式,几乎没有副作用。副作用意味着影响其他状态或对象,这可能会给调用程序员带来不愉快的惊喜。

“fetch”和“submit”

对于我自己,当有相对较大量的工作需要完成时,例如数据库调用或返回值非常动态、快速变化,如数据源,或依赖于可能会发生变化的外部因素,例如调用到JNDI资源时,我使用前缀“fetch”。例如:
fetchCurrentMonthTimeSheets( java.time.YearMonth yearMonth ) 另一方面,如果“set”类型的方法需要执行大量工作,具有副作用,或者其结果高度动态或不太可预测,则我使用“submit”作为前缀。例如:
submitTimeSheetForPayroll( TimeSheet timeSheet ) 我还在可能出现null值或异常的情况下使用它们。get/set用于快速简单易用,而fetch/submit用于复杂重型情况。
java.time约定
Java 8及其后续版本中的java.time框架的构建者在其命名约定方面投入了大量思考,如Oracle教程所述。我可以想象其他库可能会遵循他们的指南。
他们定义了11个具有特定含义的词语。一些是实例方法,一些是静态(类)方法。java.time类避免使用new命令进行实例化,而是使用静态工厂方法
请记住,java.time专门使用不可变对象进行清晰的逻辑和自动线程安全。当然,不总是适用于不可变性,因此此列表可能或可能不适合您自己的设计。

静态工厂方法

  • of
    创建一个实例,工厂主要验证输入参数,而不是转换它们。
  • from
    将输入参数转换为目标类的实例,可能会丢失输入信息。
  • parse
    解析输入字符串以生成目标类的实例。

实例方法

  • format
    使用指定的格式器将时间对象中的值格式化为字符串。
  • get
    返回目标对象状态的一部分。
  • is
    查询目标对象的状态。
  • with
    返回目标对象的副本,其中一个元素已更改;这是JavaBean上set方法的不可变等价物。
  • plus
    返回添加了一定时间量的目标对象的副本。
  • minus
    返回减去一定时间量的目标对象的副本。
  • to
    将此对象转换为另一种类型。
  • at
    将此对象与另一个对象组合。

示例用法

如果这些描述不够清晰,请查看java.time类以了解它们的使用方法。以下是一些示例代码,使用了toofatwithget
从旧式过时的java.util.Date类转换为java.time.Instant
Instant instant = myUtilDate.toInstant(); 

指定一个时区以获取一种新的对象,ZonedDateTime
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( zoneId );

调整到另一个星期几。
ZonedDateTime followingTuesday = zdt.with( TemporalAdjustors.next( DayOfWeek.TUESDAY ) );

逐部分解释:年份、月份和日期。
int year = zdt.getYear();
int month = zdt.getMonthValue(); // Or handy Month enum…  zdt.getMonth();
int dayOfMonth = zdt.getDayOfMonth();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接