Реализация стандартных шаблонов проектирования программного обеспечения (фокус на MVC) в R
-
12-12-2019 - |
Вопрос
В настоящее время я много читаю о разработке программного обеспечения, дизайна программного обеспечения, шаблоны дизайна и т. Д., Выходя из совершенно другого фона, это все новые увлекательные вещи для меня, поэтому, пожалуйста, имейте со мной в случае, если я не использую правильная техническая терминология для описания определенных аспектов; -)
Я закончил использовать Справочные классы (способ OOP в R) большую часть времени, потому что ориентация объекта, кажется, является правильным выбором для многих вещей, которые я делаю.
Теперь мне было интересно, есть ли у кого-нибудь хороший совет или некоторый опыт в отношении реализации
Я также был бы очень заинтересован в информации относительно других «стандартных» шаблонов проектирования, таких как Observer , доске и т. Д., Но я не хочу делать это слишком широким вопросом. Я думаю, что самая крутая вещь будет видеть какой-либо минимальный пример код, но любой указатель, «схема», диаграмма или любая другая идея также будет высоко оценена!
Для тех, кто заинтересован в подобных вещах, я действительно могу порекомендовать следующие книги:
- Прагматичный программист
- Дизайн-шаблоны
- Это несколько правильная реализация шаблона MVC? Если нет, что я сделал не так?
- следует «обработать» методы (например, совокупные данные, принять подъёмы и т. Д.) Для модели «принадлежат» к модели или классу контроллера. До сих пор я всегда определил все, что конкретный объект может «делать» как методы этого самого объекта.
- Должен ли контроллер своего рода «прокси», управляющий каждое взаимодействие между моделью и представлениями (вроде «оба путей»), либо ли он отвечает только за размножив пользовательский ввод в модель (вроде одного способа »? < / li>
<Сильное> Обновление 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., контроллер).
Открытые вопросы
Вот то, что я чувствую, что я пока не полностью понимаю:
Решение
-
Это выглядит довольно хорошо, но я не так уверен, почему у вас есть наблюдатель, дополнительно для ваших других классов (может быть, вы можете сказать мне) Обычно контроллер является наблюдателем. Это действительно хорошая идея сделать это в R, потому что когда я узнал его в Java, это было не так легко понять (Java скрывает некоторые из хороших частей)
-
Да и Нет. Есть много разных интерпретаций этой картины. Мне нравится иметь методы в объекте, я бы сказал, что это принадлежит к модели. Простой пример будет Sudoku Solver, который показывает решающие шаги в графическом интерфейсе. Давайте разделим его на некоторые части, которые могут быть разделены на M, V и C: необработанные данные (может быть, 2D-массив, может быть), функции Sudoku (Calc следующий шаг, ...), графический интерфейс, кто-то, кто говорит ГЭИ, что новый Шаг был рассчитан Я бы поставил это: M: RAW DATA + SUDOKU Функции C: Кто говорит графический интерфейс об изменениях / модели о входах GUI, V: GUI без какой-либо логики Другие помещают функцию судоку в контроллер, также верно и могут работать лучше для некоторых проблем
-
Можно иметь контроллер «один путь», как вы называете его, и вид является наблюдателем модели Также можно позволить контроллеру сделать все, а модель и вид, не знают друг друга (посмотрите на модель видного презентатора, вот об этом)