Шаблон фабрики Scala

Пытаясь написать более тестируемый Java-код, я использовал шаблон Model-View-Presenter, который Мартин Фаулер опубликовал в своем блоге несколько лет назад (http://martinfowler.com/eaaDev/ModelViewPresenter.html -- да, я знаю, что он устарел, но мне все равно нравится простота).

Я создаю интерфейс View для каждого JFrame, JDialog и т. д. и использую Factory для фактического их создания, чтобы я мог создавать макеты для модульного тестирования.

Ниже приведен небольшой набор примеров классов и интерфейсов. Есть ли лучший способ в Scala, чем прямой перевод синтаксиса? Другими словами, как мне использовать трейты, ссылки на собственный тип и т. д., чтобы лучше следовать принципам 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);
    }
}

ОБНОВЛЕНИЕ:: я понял, что задал похожий вопрос в прошлом году и получил другой «лучший» ответ. Посмотрите ответ sblundy - очень приятно.


person Ralph    schedule 11.03.2011    source источник
comment
Все эти отражения, которые вы делаете, довольно излишни (даже в Java). Вы должны просто переопределить newView в DialogFactory и напрямую вызвать конструктор Dialog. Это тот же объем кода, что и написание конструктора DialogFactory прямо сейчас, а newView вызовет меньше исключений.   -  person Ken Bloom    schedule 11.03.2011
comment
@Ken Bloom: я все еще пытаюсь улучшить версию Java. Спасибо за предложение. Я посмотрю на это.   -  person Ralph    schedule 11.03.2011


Ответы (3)



Lift делает это, имея общий фабричный интерфейс, в котором есть функция Provide: Type(manifest) => Option[Type]. Это определяется так (я думаю):

trait Factory {
  def provide[T: Manifest]: Option[T]
}
person Anonymous    schedule 11.03.2011

Помимо того факта, что вы вызываете setInstance для создания DialogFactory.getInstance с макетом DialogFactory, вы можете сделать object Dialog фабрикой для создания Dialog.

person Ken Bloom    schedule 11.03.2011
comment
Да, но я не могу придумать другого способа издеваться над диалогом. - person Ralph; 11.03.2011