我有一个库,用于解析URL并提取一些数据。每个URL有一个类。要知道哪个类应该处理用户提供的URL,我有下面的代码。动态调用工厂中的正确实现
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
的问题是用户请求多个URL被解析,这意味着我的switch语句越来越大。每次有人想出解析器时,我都必须修改我的代码以包含它。
为了达到这个目的,我决定创建一个映射并将它展示给它们,以便当它们的类写入时,它们可以向工厂注册自己,通过提供主机名和提取器到工厂。下面是实施这个想法的工厂。
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
而且用户会用这样的说法:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
我拍着我自己回来的时候,我意识到这不会有任何效果:用户类在我收到URL时没有加载,只有工厂,这意味着它们的构造函数永远不会运行,并且地图总是空的。所以我回到了绘图板上,但希望得到这个工作的一些想法或另一种方法来摆脱这个烦人的开关语句。
小号
您可以使用**反射**,或者您必须列出所有已知或“想要可用”的类别,例如,一个配置文件...例如'org.reflections'是一个很好的轻量级库,用于扫描类路径。 – dedek
在一个简单的文本文件中列出它们可以自行维护的类名,将工厂构造移动到一个静态构造函数并在所有条目中使用'Class.forname'(https://stackoverflow.com/q/8100376/13075)在工厂初始化方法中的文件。 – Henrik
或者:引入它们应用于其类的注释,并在工厂中扫描类路径。 – Henrik