基于标识符选择类的设计

时间:2014-10-31 23:54:54

标签: java design-patterns groovy

我有一组实现公共DataSource接口的数据解析器。我想要一个具有以下签名的解析方法:

public static DataSource parseData(InputStream contents, String identifier)

它应该使用要解析的数据和标识符并使​​用适当的DataSource实现。每个DataSource负责一个标识符。我敢打赌,有一种比这更优雅的方式:

public static DataSource parseData(InputStream contents, String identifier) {
    if (DataSource1.respondsTo(identifier) {
        return new DataSource1(contents);
    }
    //more ifs. There likely will be about 20 of those. 
}

但我真的无法想到更好的事情。这里有适当的设计模式吗?某种链式探测器列表?

我在Groovy中这样做,但欢迎基于Java的响应。

2 个答案:

答案 0 :(得分:3)

给出以下DataSource类:

interface DataSource {
    boolean respondsTo(String identifier)
}

class DataSource1 implements DataSource {
    DataSource1(InputStream is) { /* magic goes here */ }
    @Override boolean respondsTo(String identifier) { identifier in ["DS1 idX", "DS1 idY", "DS1 idZ"] }
}

class DataSource2 implements DataSource {
    DataSource2(InputStream is) { /* magic goes here */ }
    @Override boolean respondsTo(String identifier) { identifier in ["DS2 idX", "DS2 idY", "DS2 idZ"] }
}

// ...

class DataSource20 implements DataSource {
    DataSource20(InputStream is) { /* magic goes here */ }
    @Override boolean respondsTo(String identifier) { identifier in ["DS20 idX", "DS20 idY", "DS20 idZ"] }
}

此解决方案使用enum来帮助将每个identifier字符串映射到生成DataSource的闭包中。

enum DataSourceEnum {
    ds1  (["DS1 idX",  "DS1 idY",  "DS1 idZ"],  { is -> new DataSource1(is) }),
    ds2  (["DS2 idX",  "DS2 idY",  "DS2 idZ"],  { is -> new DataSource2(is) }),

    // ...

    ds20 (["DS20 idX", "DS20 idY", "DS20 idZ"], { is -> new DataSource20(is) })  

    private final static Map<String, DataSourceEnum> dsMapping = [:]

    final Closure<DataSource> buildDataSource

    private DataSourceEnum(List<String> identifiers, Closure<DataSource> ctor) {
        DataSourceEnum.dsMapping += identifiers.collectEntries { id -> [(id):this] }
        this.buildDataSource = ctor
    }

    static DataSourceEnum identify(String id) { dsMapping[id] }
}

现在编写所需的parseData方法几乎非常简单:

DataSource parseData(InputStream contents, String identifier) {
    DataSourceEnum.identify(identifier)?.buildDataSource(contents)
}

答案 1 :(得分:0)

我意识到BalRog的答案就是你想要的,但我忍不住抛出一个反思的答案。

如果您确定与类名相对应的标识符,例如idX - &gt; ParsersidX,你可以这样做(免责声明 - 我没有编译这个 - 肯定有一些尝试/捕获必要):

public static DataSource parseData(InputStream contents, String identifier) {
    Class dataSourceClass = Class.forName("Parsers" + identifier);
    Constructor dataSourceConstructor = dataSourceClass.getDeclaredConstructor(Class.forName(InputStream));
    return dataSourceConstructor.newInstance(contents);
}

最后,我在评论中看到,这不是一对一的比赛,所以这种方式可能不够?