我希望有一个抽象类,比如说Foo,它提供了一组由子类实现的API。但我不希望任何人知道哪个是这个子类的实际实例,只是可以使用静态Foo方法检索它们。课程大致如下:
class Foo {
protected Hashtable foos = new Hashtable;
public static Foo getFoo(String key) {
return (Foo) foos.get(key);
}
public abstract void doSomething();
}
class FooA extends Foo {
static {
Foo.foos.put("A", new FooA());
}
public void doSomething() {
System.out.println("A FooA instance is doing something!!");
}
}
class FooB extends Foo {
static {
Foo.foos.put("B", new FooB());
}
public void doSomething() {
System.out.println("A FooB instance is doing something!!");
}
}
用法如下:
Foo foo = Foo.get("A");
foo.doSomething();
这个问题是因为FooA和FooB没有直接在任何地方使用,所以没有加载类,也没有执行静态块。我可以通过多种方式明确加载它们,但关键是要有“未知”的多个实现。
有些人可能认为解决方案是没有未知的实现,但这确实是我的问题的必要条件。
答案 0 :(得分:0)
如果他们以某种方式注册到基类,您可以加载“未知”注册(服务)。各种运行时容器提供了各种注册服务的方法,但JDK6以来JDK中可用的标准方法是使用ServiceLoader.load(Foo.class)(http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html处的javadoc)。我使用这种技术来实现一些我称之为可注射单例的东西(其中单例API不知道其实际实现的位置,请参阅http://wiki.apidesign.org/wiki/Injected_Singleton),但该技术也可以在您的示例中使用。
请确保 META-INF /服务/ your.pkg.Foo 文件在您的JAR中,内容提到以下两行(可以使用@ org.openide.util.lookup.ServiceProvider注释生成,但不必生成):
your.pkg.FooA
your.pkg.FooB
然后你可以使用
for (Foo instance : ServiceLoader.load(Foo.class)) {
/* do something with instance of FooA and FooB */
}
答案 1 :(得分:0)
作为替代解决方案(取决于问题的复杂性),您可能更容易使用依赖注入。在这种模式中,您只需定义接口的实现,并为它们分配限定符,然后让DI框架将实际的实现“注入”需要它们的类。客户端类不仅不知道他们正在使用什么具体实现,他们也不关心如何获取它们,这可以简化很多事情。已经有很多这样的框架(最值得注意的是Spring和Guice),java 5+有标准化的注释集,用于定义注入规则。