我有一个已经实现了50次的界面,我的应用程序将继续随着新的实现而发展。这些实现应该根据它们的名称来加载(这是每个实现中可用的常量)。
我想避免在运行时使用反射(因为反射库拉3Mb的依赖关系,我需要保持我的jar尽可能小),我也想避免在每次添加实现时向我的工厂添加一个条目。
所以我想知道:我如何在编译时自动做到这一点?我基本上只需要建立一个Implmentation.NAME => ImplmentationConstructor
的地图
编辑:我在这里真正寻找的是不必关心编写代码来加载这些类。因此,这可能意味着在编译时自动生成工厂(完整代码生成),或者使用某种类似serviceloader的工具,该工具支持自动生成所需的文件,并支持带参数的构造函数。目前,我能想到的最简单的解决方案是在我的单元测试中使用反射来检查所有的实现都可以通过我的构造函数访问,如果不能,在控制台中输出需要放在我的工厂中的代码,以便它们可以访问。
Java ServiceLoader可用于管理(并从中选择)一个接口的大量实现。添加一个以接口命名的文件,其中包含所有实现的完整限定名:-
META-INF/services/com.my.interface.MyInterface
ServiceLoader可以为你加载和管理所有的实现。在你的工厂方法中,你可以选择最合适的实现并返回它。
ServiceLoader<MyInterface> impls = ServiceLoader.load(MyInterface.class);
for(MyInterface impl : impls) {
//iterating over each impl
}
使用enum
s可以轻松灵活地完成经典工厂模式。
// What my factory can make.
interface Stuff {
}
// Some concrete implementations of Stuff.
private static class RollsRoyce implements Stuff {
public RollsRoyce() {
}
}
private static class Boeing implements Stuff {
public Boeing() {
}
}
// Each element in the factory must be able to make Stuff.
interface MakesStuff {
Stuff makeOne();
}
enum Factory implements MakesStuff {
Car {
@Override
public Stuff makeOne() {
return new RollsRoyce();
}
},
Plane {
@Override
public Stuff makeOne() {
return new Boeing();
}
};
// Special for making from a String name.
public static Stuff make(String type) {
// Will throw an exception if the name is not recognised.
return Factory.valueOf(type).makeOne();
}
}