Фабрика Скала - PullRequest
       26

Фабрика Скала

0 голосов
/ 11 марта 2011

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

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

Ниже приведенонебольшой набор примеров классов и интерфейсов.Есть ли лучший способ в Scala, чем прямой синтаксический перевод?Другими словами, как мне использовать черты, ссылки на собственные типы и т. Д., Чтобы лучше следовать принципам СУХОГО и при этом писать код, безопасный для типов?

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 - очень приятно.

Ответы [ 3 ]

3 голосов
/ 11 марта 2011

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

trait Factory {
  def provide[T: Manifest]: Option[T]
}
3 голосов
/ 11 марта 2011

Я бы взглянул на торт .Обычно он используется для полного внедрения зависимостей, а не просто для абстрагирования конструкции объекта, но он также может обеспечить это.Основная идея заключается в том, что вы объединяете конфигурацию своего приложения в черту, которую затем смешиваете вместе, чтобы создать настройки времени выполнения и тестирования:

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 голосов
/ 11 марта 2011

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

...