Chi*_*ron 6 java oop inheritance design-patterns immutability
最近,我就这个问题进行了激烈的讨论.
让我们说我用Java创建了这个方法:
public Set<String> getRich() {
return ImmutableSet<String> ....;
}
Run Code Online (Sandbox Code Playgroud)
每当我在拉动请求中看到它时,我会大喊并尝试解释它为什么是错误的.通过这样做,我通过承诺他们将得到一个Set来误导我的方法的消费者.这意味着他们可以删除或添加元素.javac很乐意编译它,但会抛出RuntimeException.除此之外,它违反了"利斯科夫替代原则".
就个人而言,我总是这样做:
public ImmutableSet<String> getRich() {
return ImmutableSet<String> ....;
}
Run Code Online (Sandbox Code Playgroud)
这样,没有人会在脚下射击自己.
一种建议的方法是返回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>;