2

I am trying to get clarification around the Factory Pattern as described (with examples) here: http://www.oodesign.com/factory-pattern.html

When I attempt to implement the "class registration without reflection" example I get a null pointer exception. This is the same as has been described here:

factory method pattern with class registration produces a nullpointer exception

I understand why I am getting the null pointer exception (the HashMap is not populated by the time it is being used) and I know that I can fix it by using class.forName in the main or in a static block within the Factory implementation.

But doesn't that defeat the purpose of using this pattern? I thought the idea was that the object being created did the registration - if you are forced to manually load the class to force the static block to run, doesn't this violate the Open Close Principle?

Here is example code:

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

}

As mentioned earlier, to get it to work I can put this in the Main or the WidgetFactory class:

static {
    try {
        Class.forName(WidgetA.class.getName());
        Class.forName(WidgetB.class.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace(System.err);
    }
}

Again, can someone please clarify how this pattern should be implemented or share a technique to make this pattern work without having to update the Main or the WidgetFactory class each time a new Widget subclass is added?

Thank you for your help!

Community
  • 1
  • 1
  • Check out these tutorials, they helped me a lot: [[1](https://www.youtube.com/watch?v=ub0DXaeV6hA)] [[2](https://www.youtube.com/watch?v=xbjAsdAK4xQ)] – 0x6C38 Jul 05 '13 at 00:21
  • Are the widget names always the same as the class names? – mmirwaldt Jul 05 '13 at 00:52
  • @MrD Thank you for the link. The tutorials are useful but do not address my specific question - in the tutorial an if/else if/else construct is used in the factory class which is what I am trying to avoid. – user2551913 Jul 08 '13 at 00:24
  • @mmirwaldt No. In the example on oodesign.com they use "ID1" and "ID2" to refer to OneProduct and TwoProduct. Also please note that I don't want to use reflection. – user2551913 Jul 08 '13 at 00:26
  • +1 as I think it is a good question and has important implications on the architecture. – mmirwaldt Jul 09 '13 at 12:29

1 Answers1

0

You cannot do this without reflection as the JVM-specification enforces lazy class loading to save resources. There is neither a parameter for the JVM nor a "EagerClassLoader" (which loads all classes of all jar files). See also the answers to this question and this one and this one.

However you can load all classes of ONE package via reflection with this code:

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

As long as you only load the classes of one package with a "reasonable" number of classes, this won't take to much resources.

My sample code was inspired by this sample code which also loads the classes of subpackages as well.

Edit:

I extended my implementation with a branch for directories so that it works recursively.

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());
        }
    }
}
Community
  • 1
  • 1
mmirwaldt
  • 843
  • 7
  • 17
  • Thank you for your answer. This suggests then that the example pattern at oodesign is incomplete (at least for a java implementation). I had resigned myself to having to forcibly load the classes and I do like the approach you describe. How would the approach work with obfuscated code? From what I have seen obfuscators often create many classes in many sub-packages - but I am unsure if they are actually used or simply random (and so would never need to be loaded). – user2551913 Jul 09 '13 at 02:53
  • If they are used, the classloader will load them automatically. I would focus only on the widget classes and not their colloborators. Use a convenient package structure by putting all widget classes in the same package or use special package names or apply patterns on the package names. You can thus select packages as their names are not obfuscated as far as I know, right? You only need to extend the if-clause with selectedPackages.contains(packageName) or packageName.matches(packageNamePattern) while packageNamePattern is a regular expression. Maybe this helps you and is enough for you. – mmirwaldt Jul 09 '13 at 12:06