vista ventajas software patrones patron mvc modelo ejemplos diseño diagrama desventajas controlador r design-patterns pass-by-reference reference-class

ventajas - Implementación de patrones de diseño de software estándar(enfoque en MVC) en R



patrones de diseño de software ejemplos (1)

En la actualidad, estoy leyendo mucho sobre Ingeniería de Software, Diseño de Software, Patrones de Diseño, etc. Procedente de un entorno totalmente diferente, eso es algo nuevo y fascinante para mí, así que por favor tengan paciencia conmigo en caso de que no utilice la terminología técnica correcta. para describir ciertos aspectos ;-)

Terminé usando clases de referencia (una forma de OOP en R) la mayor parte del tiempo porque la orientación del objeto parece ser la opción correcta para muchas de las cosas que estoy haciendo.

Ahora, me preguntaba si alguien tiene algún buen consejo o alguna experiencia con respecto a la implementación del patrón MVC (Modelo View Controller, también conocido como MVP : Model View Presenter) en R, preferiblemente utilizando clases de referencia.

También me interesaría mucho la información sobre otros patrones de diseño "estándar", como el observer , el blackboard , etc., pero no quiero que sea una pregunta demasiado amplia. ¡Creo que lo mejor sería ver un código de ejemplo mínimo, pero cualquier puntero, "esquema", diagrama o cualquier otra idea también será muy apreciado!

Para aquellos interesados ​​en cosas similares, realmente puedo recomendar los siguientes libros:

  1. El programador pragmático
  2. Patrones de diseño

ACTUALIZACIÓN 2012-03-12

Eventualmente presenté un pequeño ejemplo de mi interpretación de MVC (que podría no ser del todo correcto ;-)).

Dependencias del paquete

require("digest")

Observador de definición de clase

setRefClass( "Observer", fields=list( .X="environment" ), methods=list( notify=function(uid, ...) { message(paste("Notifying subscribers of model uid: ", uid, sep="")) temp <- get(uid, .self$.X) if (length(temp$subscribers)) { # Call method updateView() for each subscriber reference sapply(temp$subscribers, function(x) { x$updateView() }) } return(TRUE) } ) )

Modelo de definición de clase

setRefClass( "Model", fields=list( .X="data.frame", state="character", uid="character", observer="Observer" ), methods=list( initialize=function(...) { # Make sure all inputs are used (''...'') .self <- callSuper(...) # Ensure uid .self$uid <- digest(c(.self, Sys.time())) # Ensure hash key of initial state .self$state <- digest(.self$.X) # Register uid in observer assign(.self$uid, list(state=.self$state), .self$observer$.X) .self }, multiply=function(x, ...) { .self$.X <- .X * x # Handle state change statechangeDetect() return(TRUE) }, publish=function(...) { message(paste("Publishing state change for model uid: ", .self$uid, sep="")) # Publish current state to observer if (!exists(.self$uid, .self$observer$.X)) { assign(.self$uid, list(state=.self$state), .self$observer$.X) } else { temp <- get(.self$uid, envir=.self$observer$.X) temp$state <- .self$state assign(.self$uid, temp, .self$observer$.X) } # Make observer notify all subscribers .self$observer$notify(uid=.self$uid) return(TRUE) }, statechangeDetect=function(...) { out <- TRUE # Hash key of current state state <- digest(.self$.X) if (length(.self$state)) { out <- .self$state != state if (out) { # Update state if it has changed .self$state <- state } } if (out) { message(paste("State change detected for model uid: ", .self$uid, sep="")) # Publish state change to observer .self$publish() } return(out) } ) )

Controlador de definición de clase y vistas

setRefClass( "Controller", fields=list( model="Model", views="list" ), methods=list( multiply=function(x, ...) { # Call respective method of model .self$model$multiply(x) }, subscribe=function(...) { uid <- .self$model$uid envir <- .self$model$observer$.X temp <- get(uid, envir) # Add itself to subscribers of underlying model temp$subscribers <- c(temp$subscribers, .self) assign(uid, temp, envir) }, updateView=function(...) { # Call display method of each registered view sapply(.self$views, function(x) { x$display(.self$model) }) return(TRUE) } ) ) setRefClass( "View1", methods=list( display=function(model, x=1, y=2, ...) { plot(x=model$.X[,x], y=model$.X[,y]) } ) ) setRefClass( "View2", methods=list( display=function(model, ...) { print(model$.X) } ) )

Definición de Clase para Representar Datos Dummy

setRefClass( "MyData", fields=list( .X="data.frame" ), methods=list( modelMake=function(...){ new("Model", .X=.self$.X) } ) )

Crear instancias

x <- new("MyData", .X=data.frame(a=1:3, b=10:12))

Investigar las características del modelo y el estado observador

mod <- x$modelMake() mod$.X > mod$uid [1] "fdf47649f4c25d99efe5d061b1655193" # Field value automatically set when initializing object. # See ''initialize()'' method of class ''Model''. > mod$state [1] "6d95a520d4e3416bac93fbae88dfe02f" # Field value automatically set when initializing object. # See ''initialize()'' method of class ''Model''. > ls(mod$observer$.X) [1] "fdf47649f4c25d99efe5d061b1655193" > get(mod$uid, mod$observer$.X) $state [1] "6d95a520d4e3416bac93fbae88dfe02f"

Tenga en cuenta que el uid del objeto se ha registrado automáticamente en el observador después de la inicialización. De esta forma, los controladores / vistas pueden suscribirse a las notificaciones y tenemos una relación 1: n.

Crear instancias de vistas y controlador

view1 <- new("View1") view2 <- new("View2") cont <- new("Controller", model=mod, views=list(view1, view2))

Suscribir

El controlador se suscribe a las notificaciones del modelo subyacente

cont$subscribe()

Tenga en cuenta que la suscripción ha sido registrada en el observador

get(mod$uid, mod$observer$.X)

Mostrar vistas registradas

> cont$updateView() a b 1 1 10 2 2 11 3 3 12 [1] TRUE

También hay una ventana de diagrama que se abre.

Modificar modelo

> cont$model$multiply(x=10) State change detected for model uid: fdf47649f4c25d99efe5d061b1655193 Publishing state change for model uid: fdf47649f4c25d99efe5d061b1655193 Notifying subscribers of model uid: fdf47649f4c25d99efe5d061b1655193 a b 1 10 100 2 20 110 3 30 120 [1] TRUE

Tenga en cuenta que ambas vistas registradas se actualizan automáticamente ya que el modelo subyacente publicó su cambio de estado en el observador, que a su vez notificó a todos los suscriptores (es decir, el controlador).

Preguntas abiertas

Esto es lo que siento que aún no entiendo completamente:

  1. ¿Es esta una implementación algo correcta del patrón MVC? Si no, ¿qué hice mal?
  2. Deberían los métodos de "procesamiento" (por ejemplo, datos agregados, subconjuntos de tomas, etc.) para el modelo "pertenecer" al modelo o la clase de controlador. Hasta ahora, siempre he definido todo lo que un objeto específico puede "hacer" como métodos de este mismo objeto.
  3. ¿Debería el controlador ser una especie de "proxy" que controla cada interacción entre el modelo y las vistas (en cierto modo "en ambos sentidos"), o solo es responsable de propagar la entrada del usuario al modelo (tipo de "unidireccional"?

  1. Se ve bastante bien, pero no estoy muy seguro de por qué tienes un observador adicional a tus otras clases (quizás puedas decirme). Usualmente el controlador es un observador. Es una muy buena idea hacer esto en R porque cuando lo aprendí en Java no fue tan fácil de entender (Java oculta algunas de las partes buenas)

  2. Sí y no. Hay muchas interpretaciones diferentes de este patrón. Me gusta tener los métodos en el objeto, diría que pertenece al modelo. Un ejemplo simple sería un solucionador de sudoku que muestra los pasos de solución en una GUI. Vamos a dividirlo en algunas partes que pueden separarse en M, V y C: los datos brutos (una matriz 2D tal vez), las funciones sudoku (calcular el siguiente paso, ...), la GUI, alguien que le dice a la GUI que una nueva paso fue calculado lo pondría así: M: datos brutos + funciones sudoku, C: quien le dice a la GUI sobre cambios / el modelo sobre entradas GUI, V: GUI sin ninguna lógica otros ponen la función sudoku en el controlador, es también es correcto y podría funcionar mejor para algunos problemas

  3. Es posible tener un controlador de "una sola dirección" como lo llama y la Vista es un Observador del modelo. También es posible dejar que el Controlador haga todo y el Modelo y la Vista no se conocen entre sí (eche un vistazo al Presentador de Vistas de Modelos , eso es todo eso)