2008-12-06 70 views
9

我已經分配了一個項目來開發一組充當存儲系統接口的類。要求是該類支持GET方法具有以下簽名:如何避免在流量控制中使用異常?

public CustomObject get(String key, Date ifModifiedSince) 

基本方法應該返回與key相關的CustomObject當且僅當對象被ifModifiedSince後修改。如果存儲系統不包含key,那麼該方法應該返回null。

我的問題是這樣的:

如何處理其中關鍵的存在,但該對象的情景已經沒有被修改?

這是重要的,因爲一些使用此類應用將Web服務和Web應用程序。這些應用程序需要知道是否返回404(未找到),304(未修改)或200(確定,這裏是數據)。

我衡器解決方案是:

  1. 引發自定義異常時 存儲系統不包含 key
  2. 引發自定義異常時 ifModifiedSince失敗。
  3. 將狀態屬性添加到CustomObject。要求呼叫者檢查財產。

我不開心的任何這三個選項。我不喜歡選項1和2,因爲我不喜歡使用異常來控制流量。當我的意圖是指示存在沒有值時,我也不喜歡返回值。

不過,我傾向於選擇3

有沒有我不考慮的選項?有沒有人對這三種選擇有強烈的感受?


這個問題的答案,意譯:

  1. 提供一個contains 方法和要求呼叫者呼叫get(key, ifModifiedSince)之前調用它 ,拋出 異常,如果鍵不存在, 返回NULL如果對象未被修改,則修改爲 。
  2. 裹的響應和數據(如果有的話) 在複合對象。
  3. 使用預定義常量表示某個狀態(UNMODIFIED, KEY_DOES_NOT_EXIST)。
  4. 調用者實現接口爲 用作回調。
  5. 設計很糟糕。

爲什麼我不能選擇答案#1

我同意這是理想的解決方案,但它是一個我已經(勉強)被解僱。這種方法的問題在於,在大多數使用這些類的情況下,後端存儲系統將是第三方遠程系統,如Amazon S3。這意味着,一個contains方法將需要往返存儲系統,在大多數情況下,這個過程需要再次往返。因爲這將花費時間和金錢,它不是一個選項。

如果不是這個限制,這將是最好的方法。

(我知道我沒有提到的問題的重要元素,但我試圖保持它簡短顯然,這是相關的。)


結論:

在閱讀完所有答案後,我得出結論:在這種情況下,包裝是最好的方法。本質上我會模仿HTTP,元數據(頭文件)包括響應代碼和內容主體(消息)。

回答

7

這聽起來像你實際上想要返回兩個項目:響應代碼和找到的對象。你可能會考慮創建一個輕量級的包裝器,並將它們放在一起。

public class Pair<K,V>{ 
    public K first; 
    public V second; 
} 

然後,您可以創建一個新的Pair來保存您的響應代碼和數據。作爲使用泛型的一個副作用,你可以重新使用這個包裝器來處理你實際需要的任何對象。另外,如果數據尚未過期,您仍然可以返回它,但給它一個303代碼,讓他們知道它沒有改變。 4xx系列將與null配對。

3

您可以創建一個特殊的最終CustomObject爲「標記」,以表明不變:

static public final CustomObject UNCHANGED=new CustomObject(); 

並測試一個匹配的「==」代替.equals()。

它也可能工作返回null不變並拋出一個異常不存在?如果我不得不選擇其中一個,我會選擇1個,因爲這似乎是最特殊的情況。

+1

這確實會引入一系列if,then,else檢查每次調用此方法都必須仔細考慮。除了Exceptions之外,你幾乎變得更好,至少它們是強制執行的,並且更加結構化。 – 2008-12-08 04:39:26

+0

是的,但例外引入了一系列try/catch/finally,所以淨收益只是執行,而不是性能的代價?異常處理更加冗長,並且與使用代碼更加遙遠。所以......這要看...... OP問及如何避免例外。 – 2008-12-08 20:08:31

3

尋找一個不存在的對象看起來像是一個例外。再加上一個允許調用者判斷一個對象是否存在的方法,我認爲在沒有的時候拋出異常是可以的。

public bool exists(String key) { ... } 

主叫可以這樣做:

if (exists(key)) { 
    CustomObject modified = get(key,DateTime.Today.AddDays(-1)); 
    if (modified != null) { ... } 
} 

or 

try { 
    CustomObject modified = get(key,DateTime.Today.AddDays(-1)); 
} 
catch (NotFoundException) { ... } 
0

如果是可接受的,則可能返回一個放大CustomObject(包裝),其載有表示的對象和其變形狀態下,如果任何等的值。

2

與例外的問題是他們是爲了一個信號「快速失敗」的情況(即,如果沒有處理,異常會停止的應用程序),由於電子xceptional 和異常行爲。

我不認爲「密鑰存在但對象沒有被修改的情況」是一個例外,當然不是一個不正常的情況。

因此,我不會使用異常,而是我會記錄調用者爲了正確解釋結果(屬性或特殊對象)而需要執行的操作。

+0

Yeap,在這種情況下,一個不好的決定是個例外。 – OscarRyz 2008-12-06 01:09:53

+0

鑰匙從哪裏來?如果你有鑰匙,那麼對象應該存在,如果沒有,那麼這是一個特例。 – tvanfosson 2008-12-06 01:42:16

+0

@ Buzzer:可能是這樣,但是應用程序必須*停止*如果您的異常未經檢查?我不這麼認爲。 – VonC 2008-12-06 10:42:05

1

該方法簽名的要求有多嚴格?

看起來好像您正在研究一個仍在進行中的項目。如果您的班級的消費者是其他開發人員,您是否可以說服他們說他們要求的方法簽名不夠?也許他們還沒有意識到,應該有兩種獨特的失敗模式(鍵不存在,對象沒有被修改)。

如果這是一個選項,我會與您的主管討論。

5

根據給定的要求,你不能這樣做。

如果設計合同,然後添加一個條件,使主叫方調用

exists(key): bool 

服務實現是這樣的:

if (exists(key)) { 
    CustomObject o = get(key, ifModifiedSince); 
    if (o == null) { 
     setResponseCode(302); 
    } else { 
     setResponseCode(200); 
     push(o); 
    } 

} else { 
     setResponseCode(400); 
} 

客戶保持不變,並不會注意到你已經預先驗證過。

如果你沒有設計合同可能有一個很好的理由,或者它可能只是設計師(或建築師)的錯。但既然你不能改變它,那麼你也不必擔心。

那麼你應該堅持規範,並繼續像這樣:

CustomObject o = get(key, ifModifiedSince); 

if (o != null) { 
    setResponseCode(200); 
    push(o); 
    } else { 
    setResponseCode(404); // either not found or not modified. 
    } 

好吧,你不是在這種情況下發送302,但可能就是它設計的方式。

我的意思是,出於安全考慮,服務器應該不低於返回更多信息[探頭獲得(鍵,日期)只返回null或對象]

所以不要擔心。和你的經理談談,讓他知道這個決定。也用這個決定評論代碼。如果你有建築師手中的確認這個奇怪的限制背後的理由。

很可能他們沒有看到此消息,他們可以在您的建議後修改合同。

有時在想要正確進行時,我們可能會出錯並危及我們的應用程序的安全。

與您的團隊溝通。

1

我仍然返回null。

該屬性的意圖是返回指定日期後修改的對象。如果爲沒有對象返回null是可以的,那麼對於未修改的對象肯定返回null也是可以的。

我個人會爲非修改的對象返回null,併爲不存在的對象拋出異常。這似乎更自然。

你完全正確地使用流量控制的例外,所以如果你只有這3個選項,你的直覺是正確的。

1

你可以按照的NET庫模式和所謂CustomObject.Empty自定義對象是型CustomObject(喜歡的String.Empty和Guid.Empty)有一個公共的靜態只讀字段。如果對象未被修改(函數使用者需要與它進行比較),則可以返回此值。
編輯:我纔剛剛發現,你在Java中工作,但原則仍然適用

這給你以下

的選項
  • 返回NULL如果沒有按鍵不存在。

  • 返回CustomObject.Empty如果密鑰存在但對象未被修改。

缺點是消費者需要知道空返回值和CustomObject.Empty返回值之間的差異。

也許該屬性將更加適當地稱爲CustomObject.NotModified因爲Empty實際上是爲Value類型設計的,因爲它們不能爲null。另外NotModified將更容易地傳達給消費者的領域的意義。

1

關於要求的(預期的)接口被嚴重破壞。你嘗試在一種方法中做無關的事情。這是軟件地獄之路。

1

提供回調作爲參數,其中回調類可以是事件驅動或setter驅動。

如果需要,您的類的接口定義了可能發生的各種錯誤,並將CustomObject作爲事件的參數傳入。

public interface Callback { 
    public void keyDoesNotExist(); 
    public void notModified(CustomObject c); 
    public void isNewlyModified(CustomObject c); 
    . 
    . 
    . 
} 

這樣,你讓回調接口的實現來定義事件發生時該怎麼做,你可以通過界面選擇,條件是否需要檢索對象的傳遞。最後,它減少了返回邏輯的複雜性。你的方法做了一次。 API的實現者完全不需要這樣做,因爲它已經爲他們完成了。