2016-05-30 50 views
1

我有兩個變量,foobar,它們決定了我需要創建哪種對象。目前,這兩個可以容納兩個不同的值,且對象均是這樣創建的:創建對象時如何處理二維條件?

Result createResult(int foo, int bar) { 
    if(foo == 0) { 
     if(bar == 0) return new FirstResult(); 
     if(bar == 1) return new SecondResult(); 
    }else if(foo == 1) { 
     if(bar == 0) return new ThirdResult(); 
     if(bar == 1) return new FourthResult(); 
    } 
    return null; 
} 

// Common interface for all the objects 
interface Result { 

} 

這項工作只是這四種類型,但如果有更多的類型是什麼?如何處理對象創建,以便更有效率?

+0

我會建議一個'enum'和switch-case。 –

+2

你所描述的是工廠模式的用例。 –

+0

你可以更具體一點關於你的用例嗎?解決方案取決於你想要達到的目標。 – Hugo

回答

3

if/else模式應該是非常有效的一個相當大的尺寸。但是,如果事情變得非常巨大,這種變化和可讀性受損。我建議使用正確構造函數的HashMap-Lookup來加快結果確定。這需要你創建可用於在HashMap的關鍵類:

class CreationParams { 
    private final int foo; 
    private final int bar; 

    CreationParams(final int foo, final int bar) { 
     this.foo = foo; 
     this.bar = bar; 
    } 

    // make sure to implement hashCode & equals so this class can be efficiently used in a Map 
} 

正如已經在評論中提及上面,你絕對需要實現hashCodeequals。如果你沒有實現equalshashCode它不會工作,因爲如果密鑰不相同(即使相等),hashmap查找也會失敗。

現在,您可以輕鬆地將參數組合映射到構造函數並查找結果。考慮下面這個例子:

class ResultFactory { 
    private static final Map<CreationParams, Supplier<Result>> factories = createFactoryMap(); 

    private static Map<CreationParams, Supplier<Result>> createFactoryMap() { 
     final Map<CreationParams, Supplier<Result>> result = new HashMap<>(); 
     result.put(new CreationParams(0, 0), FirstResult::new); 
     result.put(new CreationParams(0, 1), SecondResult::new); 
     // ... 
     return result; 
    } 

    Result createResult(int foo, int bar) { 
     return factories.get(new CreationParams(foo, bar)).get(); 
    } 
} 

的一點是,所有構造函數(以及委託者的構造函數)現在保存在地圖factories。查找factories.get(new CreationParams(foo, bar))一旦達到一定的臨界值就會比if語句快很多,因爲它不需要迭代所有可能的目標對象,而只需要迭代具有衝突散列的目標對象。然後,您可以立即致電get或在現實世界中,您可能希望檢查null,並在此之前拋出某種異常。

不帶Java 8

如果你是停留在一箇舊的Java版本,你基本上有兩種可能的解決方法。在這兩種情況下,您都需要像這樣創建您自己的供應商界面(從技術上講,您不需要用於反射變體的界面,因爲它只需要一個也可以直接使用的實施類):

interface Supplier { 
    Result get(); 
} 

,其中一個方法是使用反射它需要較少的源代碼:

class ReflectionSupplier implements Supplier { 
    final Class<? extends Result> clazz; 

    ReflectionSupplier(final Class<? extends Result> clazz) { 
     this.clazz = clazz; 
    } 

    public Result get() { 
     try { 
      return clazz.newInstance(); 
     } catch (InstantiationException | IllegalAccessException e) { 
      throw new Error(e); 
     } 
    } 
} 

現在你可以添加如下類地圖:

result.put(new CreationParams(0, 0), new ReflectionSupplier(FirstResult.class)); 

其他另一種方法是隻爲每個實例使用(匿名)類。好處是可以編譯時發現一堆錯誤(比如沒有默認構造函數)。缺點是這會產生大量的線。只需添加到您的地圖是這樣的:

result.put(new CreationParams(0, 0), new Supplier() { 
    @Override 
    public Result get() { 
     return new FirstResult(); 
    } 
}); 
+0

如果沒有Java 8特性(即沒有':: new'),你會怎麼做? – manabreak

+0

@manabreak:我擴展瞭如何在沒有Java 8的情況下做出答案。 – yankee

+0

應該ResultFactory.createResult()是靜態的嗎? –

1

好讓我們試試chicky

private String [][] classNames = new String[][]{ 
    {"com.something.FirstResult", "com.something.SecondResult"}, 
    {"com.something.ThirdResult", "com.something.ThirdResult"} 
}; 

Result createResult(int foo, int bar) { 
    Class<?> clazz = Class.forName(classNames[foo][bar]); 
    return clazz.newInstance(); 
} 

如果構造有參數可以是這樣的..

Class<?> clazz = Class.forName("com.something.SomeClass"); 
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class); 
Object instance = constructor.newInstance("some string", 0); 

而且洋基建議:

Class [] classes = new Class[]{FirstClass.class, SecondClass.class }; 
classes[0].newInstance() 

將是另一種方式來實現類似我的想法。

+3

爲什麼'Class.forName(「com.something.SomeClass」)'而不是簡單的'com.something.SomeClass.class'? – yankee

+0

兩者都是正確的..我只是展示了一些方法來做到這一點。如果他使用類名稱,那麼可能是數組可能來自另一個來源,如文件或數據庫哈哈。 –

1

這可能是矯枉過正,但您可以使用數據結構將foo/bar對映射到Class實例,然後在該實例上調用newInstance()來創建對象。這或多或少會如何看待使用二維數組存儲Class ES:

static Class[][] classes = new Class[][]{ 
    { FirstResult.class, SecondResult.class}, 
    { ThirdClass.class,FourthResult.class} 
} 

Result createResult(int foo, int bar){ 
    if (foo < 0 || foo >= classes.length) 
     return null; 
    if (bar < 0 || bar >= classes[foo].length) 
     Return null; 
    Try { 
     return classes[foo][bar].newInstance(); 
    } catch (InstantiationException ie) { 
     // Shouldn't happen 
    } catch (IllegalAccessException iae) { 
     // also shouldn't happen 
    } 
} 

此代碼是從我的頭頂,所以我不保證它會工作原樣,但它應該給你的一般想法...

+0

哎呀,@ dumb_terminal在那裏提前或多或少有相同的解決方案 –