Java Generic Factory模式实现

时间:2017-05-05 16:21:54

标签: java generics design-patterns factory

我正在尝试做的是实现一个点击流数据生成器,可以很好地扩展。

我在想什么:

属性是类,例如每个用户都有某种浏览器。所以有一个浏览器类。对于语言,插件等也是如此....

对于每个Property,都有一个Factory为我创建一个属性的随机实例,例如新浏览器(“Firefox”)或新语言(“德语”)。可能的值存储在每个属性的文件中。

基本上所有这些工厂和财产类都在做同样的事情。现在我为每个房产都有一个单独的工厂,每个新房子我必须建立一个新的工厂。

我的问题是,是否有可能为我拥有的所有物业以及新的物业实施某种通用工厂。

这是我的代码:

public abstract class Property {

protected String value;
Random rand;

public Property(String value) {
    this.rand = new Random();
    this.value = value;
}

@Override
public String toString() {      
    return this.value;
}
}


public class Browser extends Property{

public Browser(String value) {
    super(value);
}
}

public abstract class AbstractFactory implements IFactory{
List<String> valuesList;
FileReader fileReader;
BufferedReader bufferedReader;
Random rand;

public AbstractFactory(String inputFile) {      
    rand = new Random();
    this.valuesList = new LinkedList<String>();     
    String line = null;

    try {
        fileReader = new FileReader(inputFile);
        bufferedReader = new BufferedReader(fileReader);
        while ((line = bufferedReader.readLine()) != null) {
            valuesList.add(line);
        }
        bufferedReader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
}


import model.Browser;

public class BrowserFactory extends AbstractFactory{

public BrowserFactory(String inputFile) {
    super(inputFile);
}

@Override
public Browser getInstance() {      
    return new Browser(valuesList.get(rand.nextInt(valuesList.size())));
}

}

1 个答案:

答案 0 :(得分:0)

匈牙利表示法(技术上,系统匈牙利表示法)不受欢迎。不应使用I前缀命名Java接口。如果查看Java SE documentation,您会看到没有一个以这种方式命名的接口。

因此,考虑到这一点,您可以将Factory定义为:

public interface Factory<T> {
    T getInstance();
}

然后AbstractFactory复制:

public abstract class AbstractFactory<T> implements Factory<T> {

最后,BrowserFactory可以简单地使泛型类型更具体:

public class BrowserFactory extends AbstractFactory<Browser> {
    @Override
    public Browser getInstance() {

如果你想创建一个具体的类,你需要一些统一的方法来创建类。如果它们都具有带String的构造函数,则可以使用反射:

public class FeatureFactory<T> extends AbstractFactory<T> {
    private final Constructor<T> constructor;

    public FeatureFactory(Class<T> featureType) {
        try {
            this.constructor = featureType.getConstructor(String.class);
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException(
                "Cannot find/access (String) constructor in " + featureType, e);
        }
    }

    @Override
    public T getInstance() {
        try {
            return constructor.newInstance(
                valuesList.get(rand.nextInt(valuesList.size())));
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }
}

您必须传递一个Class对象才能工作。泛型在运行时被擦除,因此您无法仅从<T>派生类。实际上,您会发现Java SE类的工作方式相同;有关示例,请参阅EnumSetEnumMap

另一种更简洁的方法是使用Java 8 Function

public class FeatureFactory<T> extends AbstractFactory<T> {
    private final Function<String, ? extends T> creator;

    public FeatureFactory(Function<String, ? extends T> creator) {
        this.creator = Objects.requireNonNull(creator,
            "Creation function cannot be null");
    }

    @Override
    public T getInstance() {
        creator.apply(
            valuesList.get(rand.nextInt(valuesList.size())));
    }
}

可以通过以下方式调用:

FeatureFactory<Browser> browserFactory = new FeatureFactory<>(Browser::new);