Программирование GUI на Clojure сложно

Я пишу служебную программу с использованием графического интерфейса пользователя Swing. Я пытаюсь использовать модель представления Мартина Фаулера для облегчения тестирования. Мое приложение автоматически сохранит несколько пользовательских настроек, используя java.util.prefs.Preferences (то есть: положение и размер главного окна). На выходных я потратил несколько часов, пытаясь создать на Clojure имитацию Preferences API (используя EasyMock ), чтобы я мог протестировать код своего докладчика, но не смог заставить его работать. Программирование GUI на Clojure с использованием не-объектно-ориентированного стиля сложно для опытного объектно-ориентированного программирования. Я чувствую, что если я смогу обнаружить / разработать шаблоны для этих вещей (насмешки, «интерфейсы» для визуальных «классов» и т. Д.), Я смогу продолжать использовать те же шаблоны во всем остальном приложении.

У меня также есть разрабатывал то же приложение на Scala для сравнения шаблонов программирования и обнаружил, что оно гораздо более интуитивно понятное, хотя я пытаюсь использовать Scala в довольно строгом функциональном стиле (исключая, конечно, вызовы классов Java, таких как Swing API, которые будут иметь те же проблемы изменчивости в версии Clojure, но, конечно, также будут однопоточными).

В моем коде Scala я создаю класс с именем MainFrame , который расширяет JFrame и реализует трейт MainView . MainView предоставляет все вызовы JFrame как абстрактные методы, которые я могу реализовать в фиктивном объекте:

trait LabelMethods {
  def setText(text: String)
  //...
}

trait PreferencesMethods {
  def getInt(key: String, default: Int): Int
  def putInt(key: String, value: Int)
  //...
}

trait MainView {
  val someLabel: LabelMethods
  def addComponentListener(listener: ComponentListener)
  def getLocation: Point
  def setVisible(visible: Boolean)
  // ...
}

class MainFrame extends JFrame with MainView {
  val someLabel = new JLabel with LabelMethods
  // ...
}

class MainPresenter(mainView: MainView) {
  //...
  mainView.addComponentListener(new ComponentAdaptor {
    def componentMoved(ev: ComponentEvent) {
      val location = mainView.getLocation
      PreferencesRepository.putInt("MainWindowPositionX", location.x)
      PreferencesRepository.putInt("MainWindowPositionY", location.y)
  }
  mainView.someLabel.setText("Hello")
  mainView.setVisible(true)
}

class Main {
  def main(args: Array[String]) {
    val mainView = new MainFrame
    new MainPresenter(mainView)
  }
}

class TestMainPresenter {
  @Test def testWindowPosition {
    val mockPreferences = EasyMock.mock(classOf[PreferencesMethods])
    //... setup preferences expectation, etc.
    PreferencesRepository.setInstance(mockPreferences)

    val mockView = EasyMock.createMock(classOf[MainView])
    //... setup view expectations, etc.
    val presenter = new MainPresenter(mockView)
    //...
  }
}

Я использую псевдо-синглтон ( setInstance включен так что макет может заменить «настоящую» версию) для настроек, поэтому подробности не отображаются. Я знаю о шаблоне пирога, но обнаружил, что мой в этом случае немного проще использовать.

Мне было трудно сделать аналогичный код в Clojure. Есть ли хорошие примеры проектов с открытым исходным кодом, которые делают такие вещи? Я прочитал несколько книг по Clojure (Programming Clojure, The Joy of Clojure, Practical Clojure), но не видел, чтобы эти проблемы решались. Я также изучал ants.clj Рича Хики, но его использование Swing в этом примере довольно простое.

13
задан Ralph 11 April 2011 в 12:07
поделиться