Java 抽象澄清的工厂模式

Java 抽象澄清的工厂模式,java,design-patterns,factory-pattern,Java,Design Patterns,Factory Pattern,我试图澄清工厂模式,如下所述(附示例): 当我尝试实现“没有反射的类注册”示例时,我得到一个空指针异常。这与此处描述的相同: 我理解为什么会出现空指针异常(HashMap没有按使用时间填充),并且我知道可以通过在main中使用class.forName或在工厂实现中的静态块中修复它 但这不是违背了使用这种模式的目的吗?我认为这个想法是创建的对象进行了注册-如果您被迫手动加载类以强制运行静态块,这是否违反了打开-关闭原则 下面是示例代码: Main.java public class Main

我试图澄清工厂模式,如下所述(附示例):

当我尝试实现“没有反射的类注册”示例时,我得到一个空指针异常。这与此处描述的相同:

我理解为什么会出现空指针异常(HashMap没有按使用时间填充),并且我知道可以通过在main中使用class.forName或在工厂实现中的静态块中修复它

但这不是违背了使用这种模式的目的吗?我认为这个想法是创建的对象进行了注册-如果您被迫手动加载类以强制运行静态块,这是否违反了打开-关闭原则

下面是示例代码:

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;
    }
}
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());
        }
    }
}