2016-08-30 79 views
1

假設我們有一個返回我們的倉庫蘋果列表的功能:正確的方式來修改公共接口

List<Apple> getApples(); 

,我們已經發現了一個bug應用程序的某些生命週期後 - 在極少數情況下客戶此功能會導致中毒,因爲返回的一些蘋果尚未成熟。

然而,另一組客戶絕對不關心成熟度,他們只是用這個功能來了解所有可用的蘋果。

解決這個問題的天真的方法是將'成熟'成員添加到蘋果,然後找到所有成熟度可能導致問題並進行檢查的地方。

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

然而,如果我們相關具有途中類接口通常被設計成熟的蘋果的這項新規定,我們可能會發現,我們需要新的接口,它是一個更通用的一個子集:

​​

它基本上通過過濾那些不成熟的擴展getApples()接口。

所以問題是:

  1. 是這種思維正確的方式?
  2. 舊界面(getApples)應該保持不變嗎?
  3. 如果稍後我們發現某些顧客對紅/綠/黃蘋果(getRipeNonRedApples)過敏,它將如何處理縮放?
  4. 是否有任何其他替代方法來修改API?

但是有一個限制:我們如何最大限度地減少沒有經驗/不注意開發者調用getApples而不是getRipeApples的概率?使用RipeApple子類Apple?在getRipeApples中沮喪?

回答

0

恕我直言,任何可能的蘋果組合創建新的類/方法將導致代碼污染。

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); 

這將產生一個成熟,紅色美味的蘋果列表。

+0

一個(更高級的)的變化到上述溶液中。將具有標準對象,而不是標誌。標準對象將包含需要應用於所需解決方案的所有過濾器。 – MaxZoom

1

A pattern與Java人經常發現的是版本化功能的想法。

您有類似:

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,但要精確:我非常推薦爲「標誌」是像List<String>,或List<Integer>(對於「真實」的詮釋像標誌),甚至Map<String, Object>。換句話說,如果你真的不知道隨着時間的推移可能會出現什麼樣的情況,那就去尋找一切適用於所有事情的接口吧:就像你可以用不同的鍵的「鍵」和「期望值」給出一個地圖。如果你在那裏尋找純粹的枚舉,你很快會遇到類似的「版本控制」問題。

或者:考慮允許您的客戶自己進行過濾,使用的東西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);