[英]A method declaring a mutable data structure as an output and returning an immutable one actually
最近,我就这个问题进行了激烈的讨论。
让我们说我用Java创建了这个方法:
public Set<String> getRich() {
return ImmutableSet<String> ....;
}
每当我在拉动请求中看到它时,我会大喊并尝试解释它为什么是错误的。 通过这样做,我通过承诺他们将得到一个Set来误导我的方法的消费者。 这意味着他们可以删除或添加元素。 javac很乐意编译它,但会抛出RuntimeException 。 除此之外 ,它违反了“利斯科夫替代原则” 。
就个人而言,我总是这样做:
public ImmutableSet<String> getRich() {
return ImmutableSet<String> ....;
}
这样,没有人会在脚下射击自己。
一种建议的方法是返回Iterable 。 我认为这很糟糕,因为该方法的使用者将失去HashSet,HashMap或其他任何东西(通过无法调用set.get(hash))的潜在能力。
另一种方法是 - 作为消费者 - 创建getRich()方法输出的副本。 但是你如何确定消费者会这样做呢?
当然,你有那些坚持OOP设计原则的人说:“总是编程接口”。 对我来说 - 在这种特殊情况下 - 这是有史以来最糟糕的选择。
你会如何处理这个案子?
(非主题:这个案例是一个完美的例子,静态类型如何确保正确性,但永远不会确保正确性)。
从技术上讲 ,将返回类型声明为Set<T>
并不违反LSP。
Set<T>.Add
的文档明确声明实现可能会抛出UnsupportedOperationException
。 这意味着客户有责任检查该集合是否可变。
现在,是否使ImmutableSet<T>
扩展Set<T>
是一个不错的选择,这是有争议的。 我个人认为这是一个非常糟糕的选择 - 客户端代码不应该充斥着try / catches来查明一个集合是否可变。 此外, Set<T>
甚至不提供isMutable()
方法让客户端轻松执行检查! 为不可变集合设置单独的接口将是一个更清洁的设计。
我个人会解决这个设计缺陷 ,正如你的建议,将返回类型声明为ImmutableSet<T>
。
问题的第二部分是你是否应该返回ImmutableSet<T>
或Iterable<T>
:这实际上取决于你认为客户对你的API的期望。 需要考虑的事项:
Iterable<T>
; ImmutableSet<T>
吗? 如果没有,请使用Iterable<T>
;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.