Java 抽象澄清的工厂模式
我试图澄清工厂模式,如下所述(附示例): 当我尝试实现“没有反射的类注册”示例时,我得到一个空指针异常。这与此处描述的相同: 我理解为什么会出现空指针异常(HashMap没有按使用时间填充),并且我知道可以通过在main中使用class.forName或在工厂实现中的静态块中修复它 但这不是违背了使用这种模式的目的吗?我认为这个想法是创建的对象进行了注册-如果您被迫手动加载类以强制运行静态块,这是否违反了打开-关闭原则 下面是示例代码: Main.javaJava 抽象澄清的工厂模式,java,design-patterns,factory-pattern,Java,Design Patterns,Factory Pattern,我试图澄清工厂模式,如下所述(附示例): 当我尝试实现“没有反射的类注册”示例时,我得到一个空指针异常。这与此处描述的相同: 我理解为什么会出现空指针异常(HashMap没有按使用时间填充),并且我知道可以通过在main中使用class.forName或在工厂实现中的静态块中修复它 但这不是违背了使用这种模式的目的吗?我认为这个想法是创建的对象进行了注册-如果您被迫手动加载类以强制运行静态块,这是否违反了打开-关闭原则 下面是示例代码: Main.java public class Main
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;
}
}
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);
}
}
同样,是否有人可以澄清该模式应该如何实现,或者分享一种技术,使该模式能够工作,而不必在每次添加新的小部件子类时更新Main或WidgetFactory类
谢谢你的帮助 由于JVM规范强制执行惰性类加载以节省资源,因此您无法在没有反射的情况下执行此操作。JVM既没有参数,也没有“类加载器”(加载所有jar文件的所有类)。另请参见和的答案 但是,您可以使用以下代码通过反射加载一个包的所有类:
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));
}
}
}
public static void loadAllClassesOfPackage(String packageName)抛出IOException、ClassNotFoundException{
ClassLoader ClassLoader=Thread.currentThread()
.getContextClassLoader();
断言类加载器!=null;
字符串路径=packageName.replace('.','/');
枚举资源=classLoader.getResources(路径);
File packageDir=新文件(resources.nextElement().getFile());
对于(文件:packageDir.listFiles()){
if(file.isFile()&&file.getName().endsWith(“.class”)){
Class.forName(packageName)
+ '.'
+file.getName().substring(0,
file.getName().length()-6));
}
}
}
只要您只加载一个包中具有“合理”数量的类,这就不会占用太多资源。
我的示例代码的灵感来源于它,它还加载子包的类
编辑:
我用一个目录分支扩展了我的实现,这样它可以递归地工作
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());
}
}
}
publicstaticvoid安全地加载allclassesofpackager(stringpackagename)
抛出IOException,ClassNotFoundException{
ClassLoader ClassLoader=Thread.currentThread()
.getContextClassLoader();
断言类加载器!=null;
字符串路径=packageName.replace('.','/');
枚举资源=classLoader.getResources(路径);
File packageDir=新文件(resources.nextElement().getFile());
对于(文件:packageDir.listFiles()){
if(file.isFile()&&file.getName().endsWith(“.class”)){
Class.forName(packageName)
+ '.'
+file.getName().substring(0,
file.getName().length()-6));
}else if(file.isDirectory()){
以安全方式加载所有ClasseSofPackager(packageName+'.'+文件.getName());
}
}
}
查看这些教程,它们给了我很大帮助:]小部件名称是否总是与类名相同?@MrD谢谢您的链接。这些教程很有用,但没有解决我的具体问题-在教程中,工厂类中使用了if/else if/else构造,这是我试图避免的。@mmirwaldt否。在oodesign.com上的示例中,它们使用“ID1”和“ID2”来指代一个产品和两个产品。另外,请注意,我不想使用反射。+1,因为我认为这是一个好问题,对体系结构有重要影响。谢谢您的回答。这表明oodesign的示例模式是不完整的(至少对于java实现是如此)。我不得不被迫加载这些类,我确实喜欢你描述的方法。该方法如何处理模糊代码?据我所见,模糊处理程序通常在许多子包中创建许多类,但我不确定它们是实际使用的还是随机的(因此永远不需要加载)。如果使用它们,类加载器将自动加载它们。我将只关注widget类,而不是它们的colloborator。使用方便的包结构,将所有小部件类放在同一个包中,或者在包名称上使用特殊的包名称或应用模式。因此,您可以选择包,因为据我所知,它们的名称不会混淆,对吗?您只需要使用selectedPackages.contains(packageName)或packageName.matches(packageNamePattern)扩展if子句,而packageNamePattern是正则表达式。也许这对你有帮助,对你来说已经足够了。
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());
}
}
}