开发者

Scala Factory pattern

In trying to write more testable Java code, I have been using the Model-View-Presenter pattern that Martin Fowler blogged about years ago (http://martinfowler.com/eaaDev/ModelViewPresenter.html -- yeah, I know he deprecated it, but I still like the simplicity).

I create a View interface for each JFrame, JDialog, etc. and use a Factory to actually generate them so that I can generate mocks for unit testing.

Below is a small set of sample classes and interfaces. Is there a better way in Scala than a straight syntax translation? In other words, how do I use traits, self-type references, etc. to better follow DRY principles and still write type-safe code?

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 Di开发者_如何学CalogFactory();
    }

    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:: I realized that I asked a similar question last year and got a different "best" answer. Check out the answer by sblundy -- very nice.


I'd take a look at the cake-pattern. It's typically used to do full dependency-injection as opposed to just abstracting out object construction but it can provide that as well. The basic idea is you bundle up your application configuration into a trait which you then mix together to produce your runtime and testing setups:

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


Lift does it by having a general factory interface, which has a provide: Type(manifest) => Option[Type] function. It's defined like this (I think):

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


Aside from the fact that you're calling setInstance to make DialogFactory.getInstance with a mock DialogFactory, you could make object Dialog be the factory for creating Dialogs.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜