防御性拷贝:Javadoc中应该指定吗?

3
据我所了解,getter/setter 应该始终制作副本以保护数据。
但是,对于我的许多类,安全的做法是使 getter 返回所请求的属性的引用,这样以下代码就可以正常运行。
b = a.getB();
b.setC(someValue);

实际上改变了对象a的状态。如果我能证明对于我的类来说这样做没问题,那么采用这种方式实现getter是否是好的实践呢?用户是否应该被通知这一点,例如在Javadoc中?我认为这会破坏实现隐藏的范例,因此,我应该始终假设a的状态没有改变,并调用setter。

b = a.getB();
b.setC(someValue);
a.setB(b);

感谢您的提前帮助。 S
4个回答

4

好的,setC 违反了迪米特法则,所以我不认为它是最佳实践。("法则"有点过于严格 - 例如,它通常不适用于流畅接口。)

话虽如此,我认为getter不应该总是进行复制。进行深层克隆可能很昂贵。还有其他选择,比如不可变对象。

而且,现实情况下有务实的考虑。

但在JavaDoc中,我会偏向于提供过多信息。


感谢您的回答。那么在JavaDoc中陈述类似“修改返回的B实例将安全地修改此对象”这样的内容,您认为是可以接受的吗? - Sebastien
@Sebastien:是的,对于非复制引用(虽然我可能会省略“安全”这个词)。 ;) 就我个人而言,我会选择使用Brian Agnew的答案并尽可能使用包装对象或不可变对象。 - TrueWill

4
在你上面的例子中,有一个很好的论点,即因为A维护对B的引用,所以A应该照顾B,而不是将其分发出去,但代表你进行操作。否则,可以争论你正在破坏封装(因为A揭示了对B的引用),理想情况下,对象应该为您执行操作,而不是导出它们的内容以便您可以操纵它们。
话虽如此,上述做法并不罕见,通常是一种实用的选择。
当您通过get()公开对象时,有三个选项:
1. 公开实际对象 2. 进行防御性拷贝 3. 公开包装原始对象的对象,但禁止修改。例如,您可以在受限接口中包装原始对象。请参见(例如)Collections.unmodifiableCollection(),它包装原始集合(并且不复制它),但提供不允许修改的接口。
无论您做什么,都应该在接口(因此在Javadoc中)中记录它。否则,您可以随意更改它,而依赖代码很容易崩溃。

对于提及受限接口,我给出一个加分。对于.NET读者来说,可以查看List(T).AsReadOnly和ReadOnlyCollection(T)。 - TrueWill
我不知道选项3,但我真的(非常)喜欢它。非常感谢! - Sebastien
我经常创建一个只读集合的副本,并将其作为成员变量保存,以便在我的 getter 中传递它(似乎比一遍又一遍地重新生成只读列表更好)。 - Kevin Day

3
另一种选择是通过一个不可变接口公开对象。显然,这并不能百分之百地保证安全,因为调用代码总是可以将对象向下转换为可变版本,但它避免了包装对象或创建副本的任何开销。
如果我在编写自己或我的编程团队可能会使用的API时,通常会采用这种方法;也就是说,我知道“客户端”会是良好的用户!
// Immutable interface definition.
public interface Record {
  String getContent();
}

// Mutable implementation of Record interface.
public class MutableRecord implements Record {
  private final String content;

  public MutableRecord(String content) {
    this.content = content;
  }

  public String getContent() {
    return content;
  }

  public void setContent(String content) {
    this.content = content;
  }
}

// API that only exposes the object via its Record interface.
public class MyApi {
  private final MutableRecord mutableRecord;

  public Record getRecord() {
    return mutableRecord;
  }
}

-2

在你意识到之前,你就会犯下复制错误!然后你就会有一个真正的错误,而不是潜在的错误。

因此,如果你信任客户端代码,就不要担心它。

这非常学术化,我个人从未遇到过这样的问题。例如,在FindBugs(Java)中,我也立即关闭了检查...

除非出现问题,否则谁会阅读JavaDoc之类的东西?有人在那里吗?


1
除了“你会在不知不觉中弄错复制”这一点之外,我对这篇文章中的所有观点都持有不同意见。关键在于你是否信任客户端代码。如果你的方法是在公共类上公开的,通常无法控制未来的客户端。 - TrueWill
然而,我从来没有遇到过这样的问题。但你必须做你该做的事情,公平吧。 - raoulsson

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