2014-02-08 55 views
-1

讓我們假設一個公共職能:什麼取代私人功能中的非法參數異常?

public boolean doFoo(Enum e) { 
    if (e == north) { // do foo north } 
    if (e == south) { // do foo south } 
    if (e == east) { // do foo east } 
    if (e == west) { // do foo west } 

    throw new IllegalArgumentException(" illegal direction " + e); 
} 

現在讓我們說我們有doFoo私有方法,在這裏我們有超過誰通過什麼完全控制。我們知道這種情況永遠不可能。 我們仍然拋出IllegalArgumentException?我們是否返回null?

問題不在於枚舉等。代碼示例只是一個示例。問題是:如果輸入是在我們的控制之下的私有代碼中拋出非法的參數,它是如何合理的?

+1

爲什麼你會認爲有東西需要更換? –

+2

爲什麼不使用實際的'Enum'作爲方法參數?這將防止傳遞任何無效信息。 –

+2

關於如何處理錯誤情況沒有「要求」 - 您可以拋出異常,返回true/false或其他任何內容 - 只要嘗試使其與您的其他代碼處理類似情況的方式保持一致,並確保行爲被記錄。 – Krease

回答

2

它總是有道理的不信任輸入,即使你,至少在理論上,負責該輸入:

  • 你控制它現在是,但沒有保證將永遠是這種情況
  • 你相信自己永遠不會犯錯誤嗎?我當然不會(相信自己,也就是不是你)。
  • 你能否100%肯定地說,即使你現在是該代碼的唯一維護者,情況總是如此?
  • ....

沒有錯與和IllegalArgumentException,但在這種情況下,你不追趕非法輸入 - 相反,你要保護自己免受在理論上是編程錯誤不可能(這意味着,在實踐中,它們至少會發生一次)。 Java以及大多數編程語言都有這樣的功能:assertions

+0

就是我正在尋找的..到了這一點。 – JavaDeveloper

+0

在原始文章中使用該設計,最好拋出一個'IllegalStateException',因爲這個問題可能是一個更大的問題,而不是簡單地發送一個錯誤的參數,就像某人更新了枚舉而不告訴你。 – aliteralmind

+0

有爭議 - 我認爲這是一個「這意味着不可能的情況,因此我必須嚇壞」情況,即必須由斷言來處理的情況,但這完全是一個偏好問題。拋出'IllegalStateException'當然不是不正確的,在這種情況下的確可以認爲是更好的解決方案。我試圖解決問題的後面部分,這個問題不是關於'Enum',而是一般的良好編程實踐。 –

1

我這樣描述這些陳述的方式是「前後修復」陳述。你得到的是一個具有有限參數集合的枚舉,並且你希望確保某人不只是添加一個新的枚舉,而你的程序無法處理這個枚舉。

我們描述了if語句作爲開關,與Cardinality枚舉:

switch(e) { 
    case Cardinality.NORTH: 
     break; 
    case Cardinality.SOUTH: 
     break; 
    case Cardinality.EAST: 
     break; 
    case Cardinality.WEST: 
     break; 
    default: 
     throw new IllegalStateException("Not a supported state: " e.getName()); 
} 

如果枚舉永遠只能有四種情況下,那麼它只是不會有可能打的最後一個條件。

這似乎是違反直覺的,但如果有人決定你的枚舉擴展成會發生什麼:

public enum Cardinality { 
    NORTH, SOUTH, EAST, WEST, UP 
} 

等等......我們該如何應對的UP基數?看起來我們處於不受支持的狀態,所以應該拋出異常。

1
package x; 
//Enums may not be local. They must be "public enum". 
public enum Direction { 
    NORTH, SOUTH, EAST, WEST; 
} 

//---------------- 

import x.Direction; 

/** 
    <P>{@code java EnumXmpl}</P> 
**/ 
public class EnumXmpl { 
    public static final void main(String[] igno_red) { 

    } 
    public boolean doFoo(Direction e_dir) { 
    if (e_dir == Direction.NORTH) { 
     // do foo NORTH 
    } 
    if (e_dir == Direction.SOUTH) { 
     // do foo SOUTH 
    } 
    if (e_dir == Direction.EAST) { 
     // do foo EAST 
    } 
    if (e_dir == Direction.WEST) { 
     // do foo WEST 
    } 
    throw new IllegalArgumentException(" illegal direction " + e_dir); 
    } 
} 

沒有例外,這將不編譯。你會得到一個missing return statement錯誤。使用這種設計,最好拋出一個IllegalStateException,因爲它會表明一個更大的問題,而不僅僅是一個錯誤的參數被髮送 - 它意味着出了問題,比如在不知情的情況下更新和重新編譯枚舉。

你可以將其更改爲這個

public boolean doFoo(Direction e_dir) { 
    if (e_dir == Direction.NORTH) { 
     // do foo NORTH 
    } 
    if (e_dir == Direction.SOUTH) { 
     // do foo SOUTH 
    } 
    if (e_dir == Direction.EAST) { 
     // do foo EAST 
    } 
    // do foo WEST 
    } 

你也可以做一個開關由@Teo和@Makoto

一種替代方法是重新定義爲每個方向的特定功能的枚舉的建議:

package x; 
//Enums may not be local. They must be "public enum". 
public enum DirectionEnumWithDeltas { 
    NORTH(new NorthFunctionality()), 
    SOUTH(new SouthFunctionality()), 
    EAST(new EastFunctionality()), 
    WEST(new WestFunctionality()); 
    private final DirectionalFunctionality df; 
    DirectionEnumWithDeltas(DirectionalFunctionality d_f) { 
     if(d_f == null) { 
     throw new NullPointerException("d_f"); 
     } 
     df = d_f; 
    } 
    public boolean doDirectionalStuff(int i_distance) { 
     return df.doDirectionalStuff(i_distance); 
    } 
} 
abstract class DirectionalFunctionality { 
    abstract boolean doDirectionalStuff(int i_distance); 
} 
class NorthFunctionality extends DirectionalFunctionality { 
    public boolean doDirectionalStuff(int i_distance) { 
     //... 
     return true; 
    } 
} 
class SouthFunctionality extends DirectionalFunctionality { 
    public boolean doDirectionalStuff(int i_distance) { 
     //... 
     return true; 
    } 
} 
class EastFunctionality extends DirectionalFunctionality { 
    public boolean doDirectionalStuff(int i_distance) { 
     //... 
     return true; 
    } 
} 
class WestFunctionality extends DirectionalFunctionality { 
    public boolean doDirectionalStuff(int i_distance) { 
     //... 
     return true; 
    } 
} 

現在你的函數可以被重新定義爲

public boolean doFoo(DirectionEnumWithDeltas e_dir) { 
    try { 
     return e_dir.doDirectionalStuff(3); 
    } catch(NullPointerException npx) { 
     throw new NullPointerException("e_dir"); 
    } 
} 
1

是的,從私有方法中拋出IllegalArgumentException也是有意義的。

首先,您編寫的大多數公共方法也將由您自己的代碼調用,您也可以在其中控制輸入。例外的目標是發出編程錯誤信號。是否已經犯了錯誤或其他人有無關緊要:參數是非法的,所以你通過拋出一個IllegalArgumentException異常來表示它。

其次,常見的情況是將公共方法委託給私有方法。因此,即使私有方法不是由某個外部客戶端直接調用,也是由某個外部客戶端間接調用的,並且您希望通過最合適的異常(即IllegalArgumentException)來指示錯誤。