2016-05-30 42 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在那里提前或多或少有相同的解决方案 –