Pergunta

Atualmente, estou lendo muito sobre Engenharia de Software, Design de Software, Design Patterns etc.Vindo de uma formação totalmente diferente, tudo isso é algo novo e fascinante para mim, então, por favor, tenha paciência caso eu não esteja usando a terminologia técnica correta para descrever certos aspectos ;-)

acabei usando Aulas de referência (uma forma de OOP em R) na maioria das vezes porque a orientação a objetos parece ser a escolha certa para muitas das coisas que estou fazendo.

Agora, eu queria saber se alguém tem algum bom conselho ou alguma experiência com relação à implementação do MVC (Controlador de visualização de modelo;também conhecido como MVP:Model View Presenter) em R, preferencialmente usando classes de referência.

Eu também estaria muito interessado em informações sobre outros padrões de design "padrão", como observador, quadro-negro etc., mas não quero tornar essa questão muito ampla.Acho que o mais legal seria ver algum código de exemplo mínimo, mas qualquer ponteiro, "esquema", diagrama ou qualquer outra ideia também será muito apreciado!

Para aqueles interessados ​​em coisas semelhantes, posso realmente recomendar os seguintes livros:

  1. O programador pragmático
  2. Padrões de design

ATUALIZAÇÃO 12/03/2012

Acabei encontrando um pequeno exemplo da minha interpretação do MVC (que pode não estar totalmente correto ;-)).

Dependências de pacote

require("digest")

Observador de definição de classe

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 definição de classe

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 e visualizações de definição de classe

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

Definição de classe para representar dados fictícios

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

Criar instâncias

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

Investigue as características do modelo e o estado do 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"

Observe que o uid do objeto foi registrado automaticamente no observador na inicialização.Dessa forma, os controladores/visualizações podem assinar notificações e temos um relacionamento 1:n.

Instanciar visualizações e controlador

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

Se inscrever

O controlador assina notificações do modelo subjacente

cont$subscribe()

Observe que a assinatura foi registrada no observador

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

Exibir visualizações registradas

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

Há também uma janela de plotagem que é aberta.

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

Observe que ambas as visualizações registradas são atualizadas automaticamente à medida que o modelo subjacente publica sua mudança de estado para o observador, que por sua vez notifica todos os assinantes (ou seja, o controlador).

Perguntas abertas

Aqui está o que sinto que ainda não entendi totalmente:

  1. Esta é uma implementação um tanto correta do padrão MVC?Se não, o que fiz de errado?
  2. Os métodos de "processamento" (por ex.dados agregados, subconjuntos etc.) para o modelo "pertencem" ao modelo ou à classe do controlador.Até agora, sempre defini tudo o que um objeto específico pode “fazer” como métodos desse mesmo objeto.
  3. O controlador deveria ser uma espécie de "proxy" controlando todas as interações entre o modelo e as visualizações (uma espécie de "ambos os sentidos") ou é responsável apenas por propagar a entrada do usuário para o modelo (uma espécie de "unidirecional"?
Foi útil?

Solução

  1. Parece muito bom, mas não sei por que você tem um Observador adicional às suas outras classes (talvez você possa me dizer). Geralmente o Controlador É um Observador.É uma ideia muito boa fazer isso em R porque quando aprendi em Java não foi tão fácil de entender (Java esconde algumas das partes boas)

  2. Sim e não.Existem muitas interpretações diferentes desse padrão.Gosto de ter os métodos no Objeto, diria que pertence ao modelo.Um exemplo simples seria um solucionador de sudoku que mostra as etapas de resolução em uma GUI.Vamos dividir em algumas partes que podem ser separadas em M, V e C:Os dados brutos (talvez 2D Array), as funções sudoku (calc na próxima etapa, ...), a GUI, alguém que diz à GUI que uma nova etapa foi calculada que eu colocaria assim:M:dados brutos + funções sudoku, C:quem informa a GUI sobre as mudanças / o modelo sobre as entradas da GUI, V:A GUI, sem qualquer lógica, outros colocam a função sudoku no controlador, também está certo e pode funcionar melhor para alguns problemas

  3. É possível ter um controlador "de uma maneira" como você chama e a visualização é um observador do modelo que também é possível deixar o controlador fazer tudo, modelar e ver que não se conhecem (dê uma olhada no apresentador de exibição de modelo , isso é sobre isso)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top