Estoy escribiendo un programa de utilidad que utiliza una GUI de Swing. Estoy tratando de utilizar Martin Fowler's Presentation Model para facilitar las pruebas. Mi aplicación almacenará automáticamente varias preferencias de usuario usando java.util.prefs.Preferences
(es decir, posición y tamaño de la ventana principal). Pasé varias horas durante el fin de semana intentando crear un simulacro de Clojure de la API Preferences
(usando EasyMock) para poder probar el código de mi presentador, pero no pude hacerlo funcionar. La programación de la GUI de Clojure que utiliza un estilo que no sea de OO es difícil para un programador de OO de larga duración. Siento que si puedo descubrir/desarrollar patrones para estas cosas (burlas, "interfaces" para "clases" visuales, etc.), puedo seguir usando los mismos patrones en el resto de la aplicación.La programación de Clojure GUI es difícil
También he estado desarrollando la misma aplicación en Scala para comparar los patrones de programación y he encontrado que es mucho más intuitiva, aunque estoy tratando de usar Scala en un estilo funcional bastante estricto (excluyendo, por supuesto, las llamadas a las clases de Java como la Swing API, que tendrá los mismos problemas de mutabilidad en la versión de Clojure, pero, por supuesto, también tendrá un único subproceso).
En mi código de Scala, creo una clase llamada MainFrame
que extiende JFrame
e implementa el rasgo MainView
. MainView
expone todos los JFrame
llamadas como métodos abstractos que pueda poner en práctica en un objeto de burla:
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)
//...
}
}
estoy usando un pseudo-Singleton (setInstance
incluye para que una maqueta puede sustituir a la versión "real") para el Preferencias, por lo que los detalles no se muestran. Sé sobre el patrón de pastel, pero encontré que el mío es un poco más fácil de usar en este caso.
He tenido problemas para hacer un código similar en Clojure. ¿Hay algún buen ejemplo de proyectos de código abierto que hacen este tipo de cosas? He leído varios libros sobre Clojure (Programación Clojure, The Joy of Clojure, Practical Clojure), pero no he visto estos temas tratados. También estudié el ants.clj
de Rich Hickey, pero su uso de Swing en ese ejemplo es bastante básico.
@dnolen: Sí. Lo volveré a leer. Gracias. Lo que creo que me resulta más difícil es "refactorizar" mi código para funciones en lugar de clases. Es curioso, ya que hice un ** MUCHO ** de programación en C en la década de 1980. – Ralph
@Ralph: en c "función" en la mayoría de los casos significa "procedimiento". De ahí la programación de procedimiento y no funcional :) –
@Vagif Verdi: Sí. Me di cuenta de la diferencia tan pronto como comencé a aprender Clojure, pero la idea de "verbo" en lugar de "sustantivo" también se aplica a la programación de procedimiento C, hasta cierto punto. Después de todo, un "procedimiento" es una serie de operaciones que afectan a los objetos, incluso si esos objetos son realmente un estado imperativo. – Ralph