Pregunta

Actualmente, estoy leyendo mucho acerca de la Ingeniería de Software, Diseño de Software, Patrones de Diseño, etc.Viniendo de una forma totalmente diferente en el fondo, eso es todo nuevo material fascinante para mí, así que por favor tengan paciencia conmigo en caso de que no soy la correcta utilización de la terminología técnica para describir ciertos aspectos ;-)

Terminé usando Clases De Referencia (una forma de programación orientada a objetos en R) la mayoría del tiempo debido a la orientación a objetos parece ser la elección correcta para un montón de cosas que estoy haciendo.

Ahora, me preguntaba si alguien tiene algunos buenos consejos o alguna experiencia con respecto a la aplicación de la MVC (Modelo Vista Controlador;también conocido como MVP:Modelo Vista Presentador) el patrón en R, preferentemente, a través de Clases de Referencia.

También me gustaría estar muy interesado en información sobre otros "estándar" patrones de diseño como observador, blackboard etc., pero no quiero hacer esto demasiado amplio de una pregunta.Supongo que lo mejor sería ver a un mínimo de código de ejemplo, pero cualquier puntero "esquema", diagrama o cualquier otra idea también es muy apreciado!

Para aquellos interesados en ese tipo de cosas, realmente puedo recomendar los siguientes libros:

  1. La Pragmática Programador
  2. Patrones De Diseño

ACTUALIZACIÓN 2012-03-12

Me hizo finalmente venir para arriba con un pequeño ejemplo de mi interpretación de la MVC (que podría no ser totalmente correcto ;-)).

Las Dependencias Del Paquete

require("digest")

Definición De La Clase De Observador

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)
        }
    )
)

Definición De La Clase Modelo

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)
        }
    )
)

Definición de la clase de Controlador y las 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 La Clase Para Representar Datos Ficticios

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 modelo y 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 objeto del uid automáticamente ha sido registrada en el observador en la inicialización.De esa manera, y controladores de vistas puede suscribirse a las notificaciones y tenemos una relación 1:n.

Crear instancias de puntos de vista y controlador

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

Suscribirse

El controlador se suscribe a las notificaciones de modelo subyacente

cont$subscribe()

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

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

Pantalla Registrado Vistas

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

También hay un complot de la ventana que se abre.

Modificar El Modelo De

> 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 ambos registrado vistas se actualizan automáticamente en el fondo, la modelo publicó su cambio de estado para el observador, que a su vez notificado a todos los suscriptores (es decir, el controlador).

Preguntas Abiertas

Esto es lo que yo siento que no soy totalmente comprensión aún:

  1. Es este un poco la correcta implementación del patrón MVC?Si no, ¿qué hice mal?
  2. Debe de "procesamiento" de los métodos (por ejemplo,los datos agregados, tomar subconjuntos etc.) para el modelo "pertenecen" a la modelo o el controlador de clase .Hasta ahora, siempre me he definido todo lo que un objeto específico puede "hacer" como los métodos de este objeto.
  3. Si el controlador de ser una especie de "proxy" el control de cada interacción entre el modelo y las vistas (una especie de "dos lados"), o sólo es responsable de la difusión de la entrada del usuario para el modelo (una especie de "one way"?
¿Fue útil?

Solución

  1. Se ve muy bueno, pero yo no estoy tan seguro de por qué usted tiene un Observador adicional a sus otras clases (tal vez usted me puede decir) Generalmente que el Controlador ES un Observador.Realmente es una muy buena idea hacer esto en R, porque cuando me enteré de que en Java no era tan fácil de entender (Java se esconde algunas de las buenas piezas)

  2. Sí y No.Hay muchas diferentes interpretaciones de este patrón.Me gusta tener los métodos en el Objeto, yo diría que pertenece a la modelo.Un ejemplo sencillo podría ser un solucionador de sudoku que muestra los pasos de solución de problemas en una interfaz gráfica de usuario.Vamos a dividirlo en algunas partes que se pueden separar en M, V y C:los datos en bruto (matriz 2D tal vez), el sudoku funciones (calc siguiente paso, ...), la interfaz gráfica de usuario, alguien que te dice que la interfaz gráfica de usuario que un nuevo paso se calculó Yo lo pondría así:M:los datos raw + sudoku funciones, C:que dice la interfaz gráfica de usuario acerca de los cambios / el modelo acerca de la GUI de entradas, V:GUI sin ninguna lógica otros ponen el sudoku de la función en el Controlador, también está a la derecha y podría funcionar mejor para algunos de los problemas

  3. Es posible tener un "modo" del controlador como se llame y la Vista es un Observador de la modelo También es posible dejar que el Controlador de hacer todo el Modelo y la Vista no se conocen entre sí (ver Modelo Vista Presentador, que es eso)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top