2

我有以下界面

public interface Splitter<T, V> {
    V[] split(T arg);
}

以下是我用来获取拆分器实现的工厂方法实现。

工厂方法实现

public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2) {

    if (key1 == Company.class && key2 == Department.class)
        return (Splitter<T, V>) new CompanySplitterImpl();

    // more cases
}

以下是我在客户端的调用,编译良好

Splitter<Company, Department> split = getSplitter(Company.class, Department.class);

我想避免客户端代码与实现的紧密耦合。有没有办法避免硬编码类型参数,即避免Splitter<Company, Department>在被调用方使用公司和部门()并使用一些变量?有没有办法从一些外部属性文件中加载它们?

仅供参考:我不确定它在 Java 中的可行性?

4

4 回答 4

1

您可以做的一件事是让您的工厂对具体实现一无所知,而是让自己向其注册(或包含预定义列表)并询问每个实现是否可以处理这些类型。例如,给定一个类似于上面示例的预定义列表:

public class SplitterFactory {
    private Set<Splitter> splitters = new HashSet<>() {{
        add(new CompanySplitterImpl());
    }};

    public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2) 
    {
        for (Splitter splitter : splitters) {
            if (splitter.canAccept(key1, key2)) {
                return splitter;
        }       
        // no matched splitter
    }
}

显然这是一个非常幼稚的解决方案,您可以更有效地实现查找。如果您在编译时不知道您的类型,您还可以使用工厂的注册机制来在运行时包含新的类型。因为 Splitter 本身现在负责报告它可以处理的类型,所以它是完全可扩展的。

于 2015-04-18T09:35:41.260 回答
1

您可以制作一个简单的地图类,您可以将它们列出来:

public final class SplitterMap {

    private final List<SplitterType<?, ?>> list = new ArrayList<>();

    private class SplitterType<T, V> {

        private final Class<T> key1;
        private final Class<V> key2;
        private final Class<? extends Splitter<T, V>> clazz;

        private SplitterType(Class<?> key1, Class<?> key2, Class<? extends Splitter<T, V> clazz) {
            this.key1 = key1;
            this.key2 = key2;
            this.clazz = clazz;
        }

        private boolean matches(Class<?> key1, Class<?> key2) {
            return this.key1 == key1 && this.key2 == key2;
        }
    }

    public <T, V> void put(Class<T> key1, Class<V> key2, Class<? extends Splitter<T, V> clazz) {
        list.add(new SplitterType<T, V>(key1, key2, clazz));
    }

    public <T, V> Splitter<T, V> get(Class<T> key1, Class<V> key2) {
        for (SplitterType<?, ?> type : list) {
            if (type.matches(key1, key2)) {
                try {
                    return ((SplitterType<T, V>) type).clazz.newInstance();
                } catch (Exception e) {
                }
            }
        }
        return null; // not found
    }
}

然后你可以这样做:

SplitterMap map = new SplitterMap();
map.put(Company.class, Department.class, CompanySplitterImpl.class);
Splitter<Company, Department> splitter = map.get(Company.class, Department.class);
于 2015-04-18T10:26:43.943 回答
0

首先,我假设您想要类似的东西

Splitter<Company, Department> s = Splitters.getSplitter()

没有反思是不可能的,因为

  1. 类型擦除
  2. 返回类型重载在 java 中尚不可用

其次,您正在滥用 FactoryMethod 模式。应该看起来更像这样:

interface Splitter<T, V> {
    V[] split(T arg);
}

interface SplitterFactory {
    <T, V> Splitter<T, V> getSplitter();
}

class CompanySplitterFactory implements SplitterFactory {
    @Override
    public Splitter<Company, Department> getSplitter() {
        return new CompanySplitterImpl();
    }
}
于 2015-04-18T10:51:44.320 回答
0

不是一个好方法,但一种方法是:

String companyClass = "Company";
String departmentClass = "Department";
Splitter split = getSplitter(Class.forName(companyClass), Class.forName(departmentClass));//raw splitter
System.out.println(split.split(new Company()));//you could use reflection here to create instance from companyClass String.
于 2015-04-18T09:38:38.303 回答