在创建对象时处理二维条件的方法?

时间:2016-05-30 07:29:47

标签: java

我有两个变量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 {

}

这项工作只适用于这四种类型,但如果还有更多类型呢?如何处理对象创建以使其更有效?

3 个答案:

答案 0 :(得分: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);
        }
    }
}

现在您可以按如下方式向Map添加类:

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

答案 1 :(得分:1)

好吧,让我们试试小鸡

quotes = open("/Users/AquaAurelius/Documents/Home/Programming/Houston/YOUR_FILENAME.txt")

如果构造函数有参数可能类似..

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

将是实现类似于我的想法的另一种方式。

答案 2 :(得分: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
     } 
}

这段代码不在我的脑海中,所以我不保证它会按原样运行,但它应该给你一般的想法......