【问题标题】:How to write an ImmutableMap that follows the Liskov Subsitution and other SOLID principles without code smells?如何编写一个遵循 Liskov Substitution 和其他 SOLID 原则的 ImmutableMap 而没有代码异味?
【发布时间】:2015-04-25 23:45:30
【问题描述】:

answered a question 正在关注 ImmutableMap。我建议使用代理模式。

问题在于Map 包含一个put 方法,它会抛出一个UnsupportedOperationException。用ImmutableMap 替换Map 的其他实例会破坏里氏替换原则。不仅如此,还需要声明putputAll【违反接口隔离原则】

从技术上讲,没有办法用ImmutableMap 替换Map 实例,因为Map 只是一个接口。所以我的问题是:

由于Map 包含putputAll 方法,使用Map 接口创建ImmutableMap 是否会被视为破坏LSP?不实现Map 会被视为“具有不同接口的替代类”代码气味吗?如何创建一个遵守 LSP 但不包含代码异味的ImmutableMap

【问题讨论】:

  • Map.putMap.putAll(以及 removeclear)被定义为“可选操作”。此外,接口文档中指定了从实现类中抛出UnsupportedOperationException 的可能性。
  • @MickMnemonic 但这违反了interface segregation principle,其中指出“不应强迫客户端依赖它不使用的方法”。我将编辑问题以包括它也应遵循其他 SOLID 原则
  • 我的意思是,这些变异方法的契约至少在Map 接口中有很好的记录。但是你有一个非常有效的观点; Map 接口是一个 fat 接口。或许原本应该拆分成MapModifiableMap extends Map
  • 有时你必须做你必须做的事情。由于 Map 到处都在使用,因此您只需将未使用的方法放入其中。如果不发明自己的类型层次结构(您可以做到但没人会提供),您将无能为力。
  • 请参阅stackoverflow.com/questions/22050848/…,讨论关于集合变异方法是否违反 LSP 的辩论。

标签: java interface immutability liskov-substitution-principle


【解决方案1】:

在我看来,ImmutableMap 应该实现 Map。不实现Map 是个坏主意,因为有许多方法接受Map 作为参数并且只在只读意义上使用它。我不认为这确实违反了 Liskov 替换原则,因为Map 的合同清楚地表明put 是一个可选操作。

实现Map 的类必须实现put 并不理想,但另一种选择是拥有复杂的接口层次结构,每个接口只包括可能的可选方法的子集。如果有n 可选方法,则必须有2^n 接口才能涵盖所有组合。我不知道n的值,但是有一些不明显的可选操作,比如map.entrySet().iterator()返回的Iterator是否支持setValue操作。如果您将此层次结构与实际存在的接口和抽象类的层次结构(包括AbstractMapSortedMapNavigableMapConcurrentMapConcurrentNavigableMap...)相结合,您将一团糟。

所以没有完美的答案,但在我看来,最好的解决方案是让ImmutableMap 实现Map,并确保您编写的每个方法都使用Map 作为参数清楚地记录@ 的任何属性987654343@ 必须有,如果传递了错误的Map 类型会抛出异常。

【讨论】:

  • 接口层次结构遵循接口隔离原则;我更喜欢多个接口而不是单个笨重的接口。我可以想到一些 Map 发生突变的情况,但你是对的;大多数情况只涉及访问(至少在我常见的情况下)。我不会撒谎,我已经想出了这样的答案;我真的希望有某种“隐藏的宝石”可以帮助我解决这种情况。在接受之前,我会等待更多的答案;我希望你明白。如果没有出现,由于您的平静声明,您将获得我的投票:)
猜你喜欢
  • 1970-01-01
  • 2010-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-22
相关资源
最近更新 更多