La aplicación de software estándar de patrones de diseño (enfoque en MVC) en R
-
12-12-2019 - |
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:
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:
- Es este un poco la correcta implementación del patrón MVC?Si no, ¿qué hice mal?
- 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.
- 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"?
Solución
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)
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
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)