我试图澄清工厂模式,如下所述(带示例): http://www.oodesign.com/factory-pattern.html
当我尝试实现“没有反射的类注册”示例时,我得到一个空指针异常。这与此处描述的相同:
factory method pattern with class registration produces a nullpointer exception
我理解为什么我得到空指针异常(HashMap在使用时没有填充),我知道我可以通过在 class.forName 中修复它strong> main 或工厂实施中的静态块。
但这不是打败了使用这种模式的目的吗?我认为这个想法是正在创建的对象进行了注册 - 如果您被迫手动加载类以强制静态块运行,这是否违反了开放关闭原则?
以下是示例代码:
Main.java
public class Main {
public static void main(String[] args) {
Widget widgetA = WidgetFactory.getInstance().createWidget("WidgetA");
Widget widgetB = WidgetFactory.getInstance().createWidget("WidgetB");
widgetA.doSomething();
widgetB.doSomething();
}
}
Widget.java
public abstract class Widget {
protected abstract Widget createWidget();
public abstract void doSomething();
}
WidgetFactory.java
public class WidgetFactory {
private static WidgetFactory instance;
public static synchronized WidgetFactory getInstance() {
if (instance == null) {
instance = new WidgetFactory();
}
return instance;
}
private HashMap<String, Widget> widgets = new HashMap<>();
public void registerWidget(String id, Widget widget) {
widgets.put(id, widget);
}
public Widget createWidget(String id) {
Widget widget = widgets.get(id).create();
return widget;
}
}
WidgetA.java
public class WidgetA extends Widget {
static {
WidgetFactory.getInstance().registerWidget("WidgetA", new WidgetA());
}
@Override
protected Widget createWidget() {
return new WidgetA();
}
@Override
public void doSomething() {
System.out.println("WidgetA");
}
}
WidgetB.java
public class WidgetB extends Widget {
static {
WidgetFactory.getInstance().registerWidget("WidgetB", new WidgetB());
}
@Override
protected Widget createWidget() {
return new WidgetB();
}
@Override
public void doSomething() {
System.out.println("WidgetB");
}
}
如前所述,为了让它工作,我可以把它放在Main或WidgetFactory类中:
static {
try {
Class.forName(WidgetA.class.getName());
Class.forName(WidgetB.class.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace(System.err);
}
}
同样,有人可以澄清一下这个模式应该如何实现,或者分享一种技术来使这个模式工作,而不必在每次添加新的Widget子类时更新Main或WidgetFactory类吗?
感谢您的帮助!
答案 0 :(得分:0)
如果没有反射就不能这样做,因为JVM规范强制执行延迟类加载以节省资源。 JVM既没有参数也没有“EagerClassLoader”(它加载所有jar的所有类)文件)。另请参阅this question和this one以及this one的答案。
但是,您可以使用以下代码通过反射加载所有类的ONE包:
public static void loadAllClassesOfPackage(String packageName) throws IOException, ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
File packageDir = new File(resources.nextElement().getFile());
for (File file : packageDir.listFiles()) {
if (file.isFile() && file.getName().endsWith(".class")) {
Class.forName(packageName
+ '.'
+ file.getName().substring(0,
file.getName().length() - 6));
}
}
}
只要您只使用“合理”数量的类加载一个包的类,就不会占用太多资源。
我的示例代码的灵感来自this sample code,它也会加载子包类。
编辑:
我使用目录分支扩展了我的实现,以便它以递归方式工作。
public static void loadAllClassesOfPackageRecursively(String packageName)
throws IOException, ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
File packageDir = new File(resources.nextElement().getFile());
for (File file : packageDir.listFiles()) {
if (file.isFile() && file.getName().endsWith(".class")) {
Class.forName(packageName
+ '.'
+ file.getName().substring(0,
file.getName().length() - 6));
} else if (file.isDirectory()) {
loadAllClassesOfPackageRecursively(packageName + '.' + file.getName());
}
}
}