2013-07-05 40 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