Реализация стандартных шаблонов проектирования программного обеспечения (фокус на MVC) в R

StackOverflow https://stackoverflow.com//questions/9674027

Вопрос

В настоящее время я много читаю о разработке программного обеспечения, дизайна программного обеспечения, шаблоны дизайна и т. Д., Выходя из совершенно другого фона, это все новые увлекательные вещи для меня, поэтому, пожалуйста, имейте со мной в случае, если я не использую правильная техническая терминология для описания определенных аспектов; -)

Я закончил использовать Справочные классы (способ OOP в R) большую часть времени, потому что ориентация объекта, кажется, является правильным выбором для многих вещей, которые я делаю.

Теперь мне было интересно, есть ли у кого-нибудь хороший совет или некоторый опыт в отношении реализации MVC (контроллер модели моделей; также известен как mvp : модель представления модели) в R, предпочтительно с использованием эталонных классов ,

Я также был бы очень заинтересован в информации относительно других «стандартных» шаблонов проектирования, таких как Observer , доске и т. Д., Но я не хочу делать это слишком широким вопросом. Я думаю, что самая крутая вещь будет видеть какой-либо минимальный пример код, но любой указатель, «схема», диаграмма или любая другая идея также будет высоко оценена!

Для тех, кто заинтересован в подобных вещах, я действительно могу порекомендовать следующие книги:

  1. Прагматичный программист
  2. Дизайн-шаблоны
  3. <Сильное> Обновление 2012-03-12

    Я в конечном итоге придумал небольшой пример моей интерпретации MVC (что может быть не полностью правильно; -)).

    Пакетные зависимости

    require("digest")
    
    .

    Определение класса 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)
            }
        )
    )
    
    .

    Модель определения класса

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

    Контроллер определения класса и представления

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

    Определение класса для представления фиктивных данных

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

    Создание экземпляров

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

    Исследуйте характеристики модели и состояние наблюдателя

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

    Обратите внимание, что UID объекта автоматически зарегистрирован в наблюдателе при инициализации. Таким образом, контроллеры / представления могут подписаться на уведомления, и у нас есть отношения 1: N.

    Инструментальные представления и контроллер

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

    Подписаться

    Контроллер Подписки на уведомления базовой модели

    cont$subscribe()
    
    .

    Обратите внимание, что подписка была зарегистрирована в наблюдателе

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

    Показать зарегистрированные представления

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

    Есть также окно сюжета, которое открывается.

    Модифицировать модель

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

    Обратите внимание, что оба зарегистрированных представления автоматически обновляются, так как основная модель опубликовала его состояние изменений на наблюдателя, что, в свою очередь, уведомил всех подписчиков (I.E., контроллер).

    Открытые вопросы

    Вот то, что я чувствую, что я пока не полностью понимаю:

    1. Это несколько правильная реализация шаблона MVC? Если нет, что я сделал не так?
    2. следует «обработать» методы (например, совокупные данные, принять подъёмы и т. Д.) Для модели «принадлежат» к модели или классу контроллера. До сих пор я всегда определил все, что конкретный объект может «делать» как методы этого самого объекта.
    3. Должен ли контроллер своего рода «прокси», управляющий каждое взаимодействие между моделью и представлениями (вроде «оба путей»), либо ли он отвечает только за размножив пользовательский ввод в модель (вроде одного способа »? < / li>
Это было полезно?

Решение

  1. Это выглядит довольно хорошо, но я не так уверен, почему у вас есть наблюдатель, дополнительно для ваших других классов (может быть, вы можете сказать мне) Обычно контроллер является наблюдателем. Это действительно хорошая идея сделать это в R, потому что когда я узнал его в Java, это было не так легко понять (Java скрывает некоторые из хороших частей)

  2. Да и Нет. Есть много разных интерпретаций этой картины. Мне нравится иметь методы в объекте, я бы сказал, что это принадлежит к модели. Простой пример будет Sudoku Solver, который показывает решающие шаги в графическом интерфейсе. Давайте разделим его на некоторые части, которые могут быть разделены на M, V и C: необработанные данные (может быть, 2D-массив, может быть), функции Sudoku (Calc следующий шаг, ...), графический интерфейс, кто-то, кто говорит ГЭИ, что новый Шаг был рассчитан Я бы поставил это: M: RAW DATA + SUDOKU Функции C: Кто говорит графический интерфейс об изменениях / модели о входах GUI, V: GUI без какой-либо логики Другие помещают функцию судоку в контроллер, также верно и могут работать лучше для некоторых проблем

  3. Можно иметь контроллер «один путь», как вы называете его, и вид является наблюдателем модели Также можно позволить контроллеру сделать все, а модель и вид, не знают друг друга (посмотрите на модель видного презентатора, вот об этом)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top