Question

Actuellement, je lis beaucoup sur l'ingénierie logicielle, la conception de logiciels, les motifs de conception, etc. provenant d'un contexte totalement différent, c'est tout nouveau des choses fascinantes pour moi, alors veuillez supporter avec moi au cas où je n'utilise pas le Terminologie technique correcte pour décrire certains aspects; -)

J'ai fini par utiliser classes de référence (un moyen d'OOP dans R) la plupart du temps car l'orientation objet semble être le bon choix pour beaucoup de choses que je fais.

Maintenant, je me demandais si quelqu'un a de bons conseils ou une certaine expérience en matière de mise en œuvre de la MVC (contrôleur de vue de modèle; aussi connu sous le nom de présentateur de mode MVP : Présenteur de modèle) dans R, de préférence en utilisant des classes de référence .

Je serais également très intéressé par des informations sur les autres modèles de conception "standard" tels que Observer , Blackboard , etc., mais je ne veux pas faire de cette question trop large. Je suppose que la chose la plus cool serait de voir un exemple de code minimal, mais tout pointeur, "schéma", diagramme ou toute autre idée sera également grandement apprécié!

Pour les personnes intéressées par des éléments similaires, je peux vraiment recommander les livres suivants:

  1. Le programmateur pragmatique
  2. motifs de conception
  3. Mise à jour 2012-03-12

    J'ai finalement parvenu à un petit exemple de mon interprétation de MVC (qui pourrait ne pas être totalement correct; -)).

    Dépendances de l'emballage

    require("digest")
    

    Observateur de définition 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)
            }
        )
    )
    

    Modèle de définition 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)
            }
        )
    )
    

    Contrôleur de définition de classe et vues

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

    Définition de la classe pour représenter des données factices

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

    Créer des instances

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

    enquêter sur les caractéristiques du modèle et l'état d'observateur

    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"
    

    Notez que l'UID de l'objet a automatiquement été enregistré dans l'observateur lors de l'initialisation. De cette façon, les contrôleurs / vues peuvent abonner aux notifications et nous avons une relation de 1: N.

    Instancier Vues et contrôleur

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

    S'abonner

    Le contrôleur s'abonne aux notifications du modèle sous-jacent

    cont$subscribe()
    

    Notez que l'abonnement a été enregistré dans l'observateur

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

    Affichage des vues enregistrées

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

    Il y a aussi une fenêtre de parcelle ouverte.

    Modifier le modèle

    > 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
    

    Notez que les deux vues enregistrées sont automatiquement mises à jour car le modèle sous-jacent a publié son évolution de l'état de l'observateur, qui a été signalé à tour tous les abonnés (c'est-à-dire le contrôleur).

    Questions ouvertes

    Voici ce que je me sens comme si je ne comprends pas encore complètement:

    1. Est-ce une implémentation quelque peu correcte du modèle MVC? Sinon, qu'est-ce que j'ai mal fait?
    2. devrait "traiter" des méthodes (par exemple, des données globales, des sous-ensembles, etc.) pour le modèle "appartiennent" au modèle ou à la classe du contrôleur. Jusqu'à présent, j'ai toujours défini tout ce qu'un objet spécifique peut "faire" comme méthodes de cet objet très.
    3. Si le contrôleur doit être en quelque sorte un "proxy" contrôlant toutes les interactions entre le modèle et les vues (en quelque sorte "des deux sens"), ou est-ce uniquement responsable de la propagation de la saisie de l'utilisateur au modèle (en quelque sorte "d'une manière"? / li>
Était-ce utile?

La solution

  1. Il a l'air assez bon, mais je ne suis pas si sûr pourquoi vous avez un observateur supplémentaire à vos autres classes (vous pouvez peut-être me le dire) généralement le contrôleur est un observateur. C'est une très bonne idée de le faire dans R parce que lorsque je l'ai appris à Java, ce n'était pas si facile à comprendre (Java cache certaines des bonnes parties)

  2. Oui et Non. Il existe de nombreuses interprétations différentes de ce modèle. J'aime avoir les méthodes de l'objet, je dirais que cela appartient au modèle. Un exemple simple serait un solveur Sudoku qui montre les étapes de résolution d'une interface graphique. Le divisons en certaines parties pouvant être séparées en M, V et C: Les données brutes (matrice 2D peut-être), les fonctions de Sudoku (étape suivante, ...), l'interface graphique qui indique à l'interface. L'étape a été calculée Je le mettrais comme ceci: M: Data Raw + Sudoku Fonctions, C: Qui indique à l'interface graphique des modifications / du modèle sur les entrées GUI, V: GUI sans aucune logique D'autres mettent la fonction Sudoku dans le contrôleur, a également raison et pourrait mieux fonctionner pour certains problèmes

  3. Il est possible d'avoir un contrôleur "aller simple" comme vous l'appelez et la vue est un observateur du modèle Il est également possible de laisser le contrôleur faire tout et que le modèle et la vision ne se connaissent pas (consultez le présentateur de vue modèle, c'est à propos de cela)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top