Domanda

Attualmente, sto leggendo molto su Software Engineering, Software Design, Design Patterns ecc. Venendo da uno sfondo totalmente diverso, questa è tutte nuove cose affascinanti per me, quindi per favore portate con me nel caso in cui non sto usando il corretta terminologia tecnica per descrivere alcuni aspetti; -)

Ho finito usando Classi di riferimento (un modo di oop in r) la maggior parte del tempo perché l'orientamento dell'oggetto sembra essere la scelta giusta per molte cose che sto facendo.

Ora, mi stavo chiedendo se qualcuno ha un buon consiglio o qualche esperienza in merito all'implementazione del MVC (Controller di visualizzazione del modello, noto anche come MVP : Modello Visualizza presentatore) Modello in R, preferibilmente usando lezioni di riferimento .

Sarei anche molto interessato a informazioni su altri modelli di design "standard" come Observer , Lavagna ecc., Ma non voglio renderlo troppo ampio di una domanda. Immagino che la cosa più bella sarebbe vedere un codice di esempio minimo, ma qualsiasi puntatore, "schema", diagramma o qualsiasi altra idea sarà molto apprezzata!

Per chi è interessato a cose simili, posso davvero raccomandare i seguenti libri:

    .
  1. Il programmatore pragmatico
  2. modelli di design
  3. Aggiornamento 2012-03-12

    Ho fatto alla fine un piccolo esempio della mia interpretazione del MVC (che potrebbe non essere totalmente corretto; -)).

    dipendenze del pacchetto

    require("digest")
    
    .

    Definizione della classe Observer

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

    Modello di definizione della 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)
            }
        )
    )
    
    .

    Controller di definizione di classe e viste

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

    Definizione di classe per la rappresentazione dei dati fittizi

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

    Crea istanze

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

    Investigare le caratteristiche del modello e lo stato dell'osservatore

    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"
    
    .

    Si noti che l'UID dell'oggetto è stato automaticamente registrato nell'osservatore sull'inizializzazione. In questo modo, i controller / viste possono iscriversi alle notifiche e abbiamo una relazione 1: N.

    Visualizzazione istanziata e controller

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

    Iscriviti

    Controller si iscrive alle notifiche del modello sottostante

    cont$subscribe()
    
    .

    Si noti che l'abbonamento è stato registrato nell'osservatore

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

    Visualizza visualizzazioni registrate

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

    C'è anche una finestra di trama aperta.

    Modifica modello

    > 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
    
    .

    Si noti che entrambe le viste registrate vengono automaticamente aggiornate in quanto il modello sottostante ha pubblicato il suo cambiamento di stato all'osservatore, che a turno ha notificato tutti gli abbonati (I.e., il controller).

    domande aperte

    Ecco cosa mi sento come non sto ancora comprendendo completamente:

      .
    1. è un'implementazione un po 'corretta del modello MVC? Se no, cosa ho fatto di sbagliato?
    2. Se "elaborare" metodi (EXTRAURARE "(E.G. DATI AGGREGAMENTI, PRENDERE ASTETSE, ecc.) Per il modello" appartengono "al modello o alla classe del controller. Finora, ho sempre definito tutto ciò che un oggetto specifico può "fare" come metodi di questo stesso oggetto.
    3. Se il controller dovrebbe essere un tipo di "proxy" che controlla ogni interazione tra modello e viste (tipo di "entrambi i modi"), o è responsabile solo di propagare l'input dell'utente al modello (tipo di "unidirezionale"? < / Li >.
È stato utile?

Soluzione

    .
  1. Sembra abbastanza buono, ma non sono così sicuro del motivo per cui hai un osservatore aggiuntivo alle tue altre classi (forse puoi dirmi) di solito il controller è un osservatore. È davvero una buona idea farlo in r perché quando l'ho imparato in Java non era così facile da capire (Java nasconde alcune delle parti buone)

  2. Sì e No. Ci sono molte diverse interpretazioni di questo modello. Mi piace avere i metodi nell'oggetto, direi che appartiene al modello. Un semplice esempio sarebbe un risolutore di Sudoku che mostra i gradini di risoluzione in una GUI. Dividiamolo in alcune parti che possono essere separate in m, v e c: i dati grezzi (forse la matrice 2D), le funzioni di Sudoku (Calc Sexy Step, ...), la GUI, qualcuno che dice alla GUI che un nuovo il passo è stato calcolato L'avrei messo in questo modo: M: DATA RAW + FUNZIONI SUDOKU, C: Chi indica la GUI Informazioni sulle modifiche / Il modello Informazioni su GUI Inputs, V: GUI senza alcuna logica Altri mettono la funzione Sudoku nel controller, è anche giusto e potrebbe funzionare meglio per alcuni problemi

  3. È possibile avere un controller "a senso unico" come chiamarlo e la vista è un osservatore del modello È anche possibile lasciare che il controller faccia tutto e modellare e visualizzare non conoscersi (dai un'occhiata al presentatore di visualizzazione modello, questo è circa questo)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top