繁体   English   中英

一种方法,将可变数据结构声明为输出并实际返回不可变数据结构

[英]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> ;
  • 否则,您可能希望遵循Robustness原则 ,该原则指出“输入类型中的逆变和输出类型中的协变”。 换句话说,为参数类型选择最抽象的类型,并为返回类型选择最具体的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM