修改公共接口的正确方法

时间:2016-08-30 21:43:04

标签: class oop interface

假设我们有一个函数返回仓库中的苹果列表:

List<Apple> getApples();

在应用程序运行一段时间后,我们发现了一个错误 - 在极少数情况下,此函数的客户会因为返回的某些苹果尚未成熟而中毒。

然而,另一组客户绝对不关心成熟度,他们使用此功能只是为了了解所有可用的苹果。

解决这个问题的天真方法是增加“成熟度”。成员到一个苹果,然后找到所有成熟可能导致问题的地方并进行一些检查。

const auto apples = getApples();
for (const auto& apple : apples)
    if (apple.isRipe())
        consume(apple)

但是,如果我们将成熟苹果的新要求与通常设计类接口的方式联系起来,我们可能会发现我们需要新接口,这是更通用的接口的一个子集:

List<Apple> getRipeApples();

基本上通过过滤那些不成熟的接口来扩展getApples()接口。

所以问题是:

  1. 这是正确的思维方式吗?
  2. 旧接口(getApples)应保持不变吗?
  3. 如果稍后我们发现有些顾客对红/绿/黄苹果过敏(getRipeNonRedApples),它将如何处理缩放?
  4. 还有其他修改API的替代方法吗?
  5. 但有一个限制因素:我们如何最大程度地降低缺乏经验/不专心的开发人员调用getApples而不是getRipeApples的可能性?使用RipeApple将Apple分类?在getRipeApples中进行向下转换?

2 个答案:

答案 0 :(得分:1)

经常与Java人员发现的pattern是版本化功能的想法。

你有类似的东西:

interface Capability ...

interface AppleDealer {
  List<Apples> getApples();
}

并且为了检索AppleDealer,有一些中央服务,如

public <T> T getCapability (Class<T> type);

所以你的客户端代码会这样做:

AppleDealer dealer = service.getCapability(AppleDealer.class);

当需要另一种方法时,你会去:

interface AppleDealerV2 extends AppleDealer { ...

想要V2的客户只需执行`getCapability(AppleDealerV2.class)调用。那些不在乎的人不必修改他们的代码!

请注意:当然,这仅适用于扩展接口。您既不能使用此方法来更改签名,也不能在现有界面中删除方法。

关于你的问题3/4:我在那里使用MaxZoom,但确切地说:我非常推荐&#34;标志&#34;类似于List<String>List<Integer>(对于&#39;真正的&#39; int就像标志)甚至是Map<String, Object>。换句话说:如果你真的不知道什么样的条件可能会随着时间推移而来,那就去寻找适用于所有事情的界面:比如你可以给地图带来&#34;键&#34;和&#34;期望值&#34;对于不同的键。如果你在那里寻找纯粹的枚举,你很快就会遇到类似的版本&#34;的问题。

或者:考虑允许您的客户自己使用like内容进行过滤;使用Java8,你可以想到Predicates,lambdas和所有这些东西。

示例:

Predicate<Apple> applePredicate = new Predicate<Apple>() {
  @Override
  public boolean test(Apple a) {
    return a.getColour() == AppleColor.GoldenPoisonFrogGolden;
  }
};

List<Apples> myApples = dealer.getApples(applePredicate);

答案 1 :(得分:0)

恕我直言,为任何可能的Apple组合创建新的类/方法将导致代码污染。您可以通过引入flags参数来优雅地处理您帖子中描述的情况:

List<Apple> getApples();      // keep for backward compatibility
List<Apple> getApples(FLAGS); // use flag as a filter

可能的标志:

  • RED_FLAG
  • GREEN_FLAG
  • RIPE_FLAG
  • SWEET_FLAG

所以可以进行如下调用:

List<Apple> getApples(RIPE_FLAG & RED_FLAG & SWEET_FLAG);

将生成一份成熟且红色的苹果列表。