假设我们有一个函数返回仓库中的苹果列表:
List<Apple> getApples();
在应用程序运行一段时间后,我们发现了一个错误 - 在极少数情况下,此函数的客户会因为返回的某些苹果尚未成熟而中毒。
然而,另一组客户绝对不关心成熟度,他们使用此功能只是为了了解所有可用的苹果。
解决这个问题的天真方法是增加“成熟度”。成员到一个苹果,然后找到所有成熟可能导致问题的地方并进行一些检查。
const auto apples = getApples();
for (const auto& apple : apples)
if (apple.isRipe())
consume(apple)
但是,如果我们将成熟苹果的新要求与通常设计类接口的方式联系起来,我们可能会发现我们需要新接口,这是更通用的接口的一个子集:
List<Apple> getRipeApples();
基本上通过过滤那些不成熟的接口来扩展getApples()接口。
所以问题是:
但有一个限制因素:我们如何最大程度地降低缺乏经验/不专心的开发人员调用getApples而不是getRipeApples的可能性?使用RipeApple将Apple分类?在getRipeApples中进行向下转换?
答案 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
可能的标志:
所以可以进行如下调用:
List<Apple> getApples(RIPE_FLAG & RED_FLAG & SWEET_FLAG);
将生成一份成熟且红色的苹果列表。