2013-07-05 120 views
2

我试图描述(举例),以获得澄清周围的工厂模式在这里: http://www.oodesign.com/factory-pattern.html与抽象工厂模式澄清后

当我尝试实施“类注册,而不反思”例如我得到一个空指针例外。这是一样的已经在这里描述:

factory method pattern with class registration produces a nullpointer exception

我明白为什么我得到空指针异常(HashMap中没有被正在使用它的时候填充),我知道我可以修复它通过使用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; 
    } 
} 

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

} 

正如前面提到的,要得到它的工作,我可以把这个在主或WidgetFactory类:

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

而且也可能有人请澄清应该如何实现这种模式或共享一种技术来使这种模式工作,而不必在每次添加新的Widget子类时更新Main或WidgetFactory类?

谢谢你的帮助!

+0

查阅这些教程,他们帮助了我很多:[1](https://www.youtube.com/watch?v=ub0DXaeV6hA)] [[2](https://开头WWW。 youtube.com/watch?v=xbjAsdAK4xQ)] –

+0

小部件名称是否总是与类名相同? – mmirwaldt

+0

@MDD感谢您的链接。这些教程是有用的,但没有解决我的具体问题 - 在教程中,我试图避免在工厂类中使用if/else if/else结构。 – user2551913

回答

0

由于JVM规范强制惰性类加载以节省资源,因此无法进行反射。既没有JVM的参数,也没有“EagerClassLoader”(加载所有类的所有jar文件)。另见this questionthis onethis 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()); 
     } 
    } 
} 
+0

谢谢你的回答。这表明oodesign中的示例模式是不完整的(至少对于java实现来说)。我已经辞职了,不得不强行加载课程,我喜欢你所描述的方法。该方法如何处理混淆代码?从我看到的混淆器经常会在许多子包中创建许多类 - 但我不确定它们是实际使用的还是随机的(因此永远不需要加载)。 – user2551913

+0

如果使用它们,类加载器将自动加载它们。我只关注窗口小部件类而不关注他们的合作者。通过将所有窗口小部件类放在同一个包中,或使用特殊的包名或在包名上应用模式,使用便利的包结构。因此,据我所知,您可以选择软件包,因为它们的名称不会被混淆,对吧?你只需要用selectedPackages.contains(packageName)或packageName.matches(packageNamePattern)扩展if-clause,而packageNamePattern是一个正则表达式。也许这对你有帮助,对你来说已经足够了。 – mmirwaldt