简体   繁体   English

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

[英]A method declaring a mutable data structure as an output and returning an immutable one actually

Lately, I'm having a heated discussion regarding this issue. 最近,我就这个问题进行了激烈的讨论。

Lets say I created this method in Java: 让我们说我用Java创建了这个方法:

 public Set<String> getRich() {
    return ImmutableSet<String> ....;
 }

Whenever I see that in a pull request, I shout and try to explain why it is wrong. 每当我在拉动请求中看到它时,我会大喊并尝试解释它为什么是错误的。 By doing that, I'm misguiding the consumers of my method by the promise that they will get a Set. 通过这样做,我通过承诺他们将得到一个Set来误导我的方法的消费者。 This means they can remove or add elements. 这意味着他们可以删除或添加元素。 javac will happily compile that but RuntimeException will be thrown. javac很乐意编译它,但会抛出RuntimeException Add to that, it is violating "Liskov Substitution Principle" . 除此之外 ,它违反了“利斯科夫替代原则”

Personally, I always do: 就个人而言,我总是这样做:

public ImmutableSet<String> getRich() {
    return ImmutableSet<String> ....;
}

This way, no one is going to shot himself in the foot. 这样,没有人会在脚下射击自己。

One suggested approach is return Iterable . 一种建议的方法是返回Iterable I think this is bad, because the consumer of that method will lose the potential power of HashSet, HashMap or whatever (by not be able to call set.get(hash) ). 我认为这很糟糕,因为该方法的使用者将失去HashSet,HashMap或其他任何东西(通过无法调用set.get(hash))的潜在能力。

Another approach is - as a consumer - to create a copy of the output of getRich() method. 另一种方法是 - 作为消费者 - 创建getRich()方法输出的副本。 But how are you going to be sure that the consumers will do that? 但是你如何确定消费者会这样做呢?

And of course, you have the folks who will stick to OOP design principles and say: "Always program to interfaces". 当然,你有那些坚持OOP设计原则的人说:“总是编程接口”。 To me - in this particular case - this is the worst option ever. 对我来说 - 在这种特殊情况下 - 这是有史以来最糟糕的选择。

How would you deal with this case? 你会如何处理这个案子?

(Off topic: This case is a perfect example how static typing might ensure correctness but it will never ensure rightfulness). (非主题:这个案例是一个完美的例子,静态类型如何确保正确性,但永远不会确保正确性)。

Technically , declaring the return type as Set<T> is not a violation of the LSP. 从技术上讲 ,将返回类型声明为Set<T>并不违反LSP。

The documentation for Set<T>.Add explicitly states that implementations might throw an UnsupportedOperationException . Set<T>.Add的文档明确声明实现可能会抛出UnsupportedOperationException Which means it's the clients responsibilities to check whether the set is mutable. 这意味着客户有责任检查该集合是否可变。

Now, whether making ImmutableSet<T> extend Set<T> was a good choice, that's debatable. 现在,是否使ImmutableSet<T>扩展Set<T>是一个不错的选择,这是有争议的。 I personally think it was a very bad choice - client code should not be littered with try/catches to find out whether a set is mutable. 我个人认为这是一个非常糟糕的选择 - 客户端代码不应该充斥着try / catches来查明一个集合是否可变。 Furthermore, Set<T> does not even provide an isMutable() method to let clients perform the check easily! 此外, Set<T>甚至不提供isMutable()方法让客户端轻松执行检查! Having a separate interface for immutable collections would be a much cleaner design. 为不可变集合设置单独的接口将是一个更清洁的设计。

I personally would work around this design flaw and, as you suggested, declare the return type as ImmutableSet<T> . 我个人会解决这个设计缺陷 ,正如你的建议,将返回类型声明为ImmutableSet<T>

The second part of your question is whether you should return ImmutableSet<T> or Iterable<T> : that really depends on what you think clients will expect from your API. 问题的第二部分是你是否应该返回ImmutableSet<T>Iterable<T> :这实际上取决于你认为客户对你的API的期望。 Things to consider: 需要考虑的事项:

  • Is your implementation likely to change? 您的实施可能会改变吗? Do you think you'll ever need to change the business logic and return some other collection? 您是否认为您需要更改业务逻辑并返回其他一些集合? If so, go with Iterable<T> ; 如果是这样,请使用Iterable<T> ;
  • Do your clients explicitly expect an ImmutableSet<T> ? 您的客户明确期望ImmutableSet<T>吗? If not, go with Iterable<T> ; 如果没有,请使用Iterable<T> ;
  • Otherwise, you might want to adhere to the Robustness principle , which states "Be contravariant in the input type and covariant in the output type." 否则,您可能希望遵循Robustness原则 ,该原则指出“输入类型中的逆变和输出类型中的协变”。 In other words, choose the most abstract type possible for your arguments types, and the most specific type possible for your return types. 换句话说,为参数类型选择最抽象的类型,并为返回类型选择最具体的类型。

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

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