2011-03-11 124 views
0

在嘗試寫更多的可測試的Java代碼,我一直使用Martin Fowler的博客上講述年前的模型 - 視圖 - 演示模式(http://martinfowler.com/eaaDev/ModelViewPresenter.html - 是啊,我知道他不贊成它,但我還是喜歡簡單)。斯卡拉工廠模式

我爲每一個的JFrame,JDialog的等視圖界面,​​並使用一個工廠實際生成它們,這樣我可以生成單元測試的嘲笑。

下面是一個小組樣本的類和接口的。斯卡拉比直接的語法翻譯有更好的方法嗎?換句話說,我如何使用特質,自我類型引用等來更好地遵循DRY原則並仍然編寫類型安全的代碼?

import java.awt.Dialog.ModalityType; 
import java.awt.Window; 
import java.awt.event.ActionListener; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.InvocationTargetException; 
import javax.swing.JButton; 
import javax.swing.JDialog; 

interface View { 
    void okButtonAddActionListener(final ActionListener actionListener); 
} 

class Dialog 
     extends JDialog 
     implements View { 
    private final JButton okButton = new JButton("OK"); 

    public Dialog(final Window owner, 
        final ModalityType modalityType) { 
     super(owner, modalityType); 
    } 

    public void okButtonAddActionListener(final ActionListener actionListener) { 
     okButton.addActionListener(actionListener); 
    } 
} 

interface ViewFactory<I, C extends I> { 
    I newView(final Window owner, 
       final ModalityType modalityType) 
      throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException; 
} 

class AbstractViewFactory<I, C extends I> 
     implements ViewFactory<I, C> { 
    private final Class<C> cls; 

    public AbstractViewFactory(Class<C> cls) { 
     this.cls = cls; 
    } 

    public I newView(final Window owner, 
        final ModalityType modalityType) 
      throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { 
     final Constructor<C> constructor = cls.getConstructor(Window.class, ModalityType.class); 

     return constructor.newInstance(owner, modalityType); 
    } 
} 

class DialogFactory 
     extends AbstractViewFactory<View, Dialog> { 
    private static final class InstanceHolder { 
     public static ViewFactory<View, Dialog> instance = new DialogFactory(); 
    } 

    public DialogFactory() { 
     super(Dialog.class); 
    } 

    public static ViewFactory<View, Dialog> getInstance() { 
     return InstanceHolder.instance; 
    } 

    public static void setInstance(final ViewFactory<View, Dialog> instance) { 
     InstanceHolder.instance = instance; 
    } 
} 

// Here is a typical usage in production 
class DialogFactoryUser { 
    private void userFactory() { 
     final Window window = new Window(null); 
     try { 
      final View view = DialogFactory.getInstance().newView(window, ModalityType.APPLICATION_MODAL); 
     } catch (final Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 
} 

// Here is a typical usage in a unit test 
class Test { 
    public void test() { 
     ... 
     mockView = createMock(View.class); 
     final Window window = new Window(null); 
     mockViewFactory = createMock(ViewFactory.class); 
     expect(mockViewFactory.newView(window, ModalityType.APPLICATION_MODAL)).andReturn(mockView); 
     ... 
     DialogFactory.setInstance(mockViewFactory); 
    } 
} 

UPDATE::我意識到,我一個similar question去年提出,並得到了不同的「最佳」答案。看看sblundy的答案 - 非常好。

+0

所有這些你正在做的反射東西是相當矯枉過正的(即使在Java中)。你應該在'DialogFactory'中重寫'newView'並直接調用'Dialog'構造函數。與編寫'DialogFactory'構造函數的代碼量相同,'newView'會拋出更少的異常。 – 2011-03-11 16:52:46

+0

@Ken Bloom:我仍然試圖改進Java版本。感謝您的建議。我會看看它。 – Ralph 2011-03-11 18:01:18

回答

3

我會看看的cake-pattern。它通常用於執行完全依賴注入,而不是僅僅抽象出對象構造,但它也可以提供。其基本思想是你捆綁你的應用程序配置爲特點,你再混合在一起,產生的運行和測試設置:

trait GUI { 
    trait View { /* ... */ } 
    def buildView(): View 
} 
/** 
* Your "real" application 
*/ 
object RealGUI extends GUI { 
    def buildView() = newView(/*...*/) 
} 
/** 
* Your mocked-up test application 
*/ 
object TestGUI extends GUI { 
    def buildView() = createMock(classOf[View]) 
} 
0
從你調用 setInstance使 DialogFactory.getInstance使用模擬 DialogFactory事實

除了,您可以讓object Dialog成爲創建Dialog的工廠。類型(清單)=>選項[類型]函數:

+0

是的,但我不能想出任何其他方式來模擬對話框。 – Ralph 2011-03-11 18:01:59

3

電梯由具有一般工廠接口,其中有一個提供做的。它的定義是這樣的(我認爲):

trait Factory { 
    def provide[T: Manifest]: Option[T] 
}