R에서 표준 소프트웨어 디자인 패턴 구현 (MVC에 중점)
현재 저는 소프트웨어 엔지니어링, 소프트웨어 디자인, 디자인 패턴 등에 대해 많이 읽고 있습니다. 완전히 다른 배경에서 온 것은 저에게 완전히 새로운 흥미로운 내용입니다. 올바른 기술 용어를 사용하지 않는 경우를 대비해 주시기 바랍니다. 특정 측면을 설명하기 위해 ;-)
내가 사용하는 결국 참조 클래스 객체 지향은 내가 뭘하는 물건을 많이에 대한 올바른 선택이 될 것 때문에 (R의 OOP의 방법) 대부분의 시간을.
이제 R 에서 MVC (Model View Controller; MVP : Model View Presenter 라고도 함 ) 패턴 을 구현하는 것과 관련하여 좋은 조언이나 경험이 있는지 궁금합니다 . 가급적 참조 클래스를 사용합니다.
관찰자 , 칠판 등과 같은 다른 "표준"디자인 패턴에 대한 정보에도 관심이 많지만이 질문을 너무 광범위하게 만들고 싶지는 않습니다. 가장 멋진 것은 최소한의 예제 코드를 보는 것입니다. 그러나 포인터, "스키마", 다이어그램 또는 기타 아이디어도 크게 감사 할 것입니다!
비슷한 내용에 관심이있는 분들을 위해 다음 책을 추천 할 수 있습니다.
업데이트 2012-03-12
나는 결국 MVC에 대한 나의 해석에 대한 작은 예를 생각해 냈습니다 (완전히 정확하지 않을 수도 있습니다 ;-)).
패키지 종속성
require("digest")
클래스 정의 관찰자
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))
Investigate model characteristics and observer state
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"
Note that the object's uid has automatically been registered in the observer upon initialization. That way, controllers/views can subscribe to notifications and we have a 1:n relationship.
Instantiate views and controller
view1 <- new("View1")
view2 <- new("View2")
cont <- new("Controller", model=mod, views=list(view1, view2))
Subscribe
Controller subscribes to notifications of underlying model
cont$subscribe()
Note that the subscription has been logged in the observer
get(mod$uid, mod$observer$.X)
Display Registered Views
> cont$updateView()
a b
1 1 10
2 2 11
3 3 12
[1] TRUE
There's also a plot window that is opened.
Modify Model
> 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
Note that both registered views are automatically updated as the underlying model published its state change to the observer, which in turn notified all subscribers (i.e., the controller).
Open Questions
Here's what I feel like I'm not fully understanding yet:
- Is this a somewhat correct implementation of the MVC pattern? If not, what did I do wrong?
- Should "processing" methods (e.g. aggregate data, take subsets etc.) for the model "belong" to the model or the controller class . So far, I always defined everything a specific object can "do" as methods of this very object.
- Should the controller be sort of a "proxy" controlling every interaction between model and views (sort of "both ways"), or is it only responsible for propagating user input to the model (sort of "one way"?
It looks quite good, but I'm not so sure why you have an Observer additional to your other classes (maybe you can tell me) Usually the Controller IS an Observer. It's a really good idea to do this in R because when I learned it in Java it was not so easy to understand (Java hides some of the good parts)
Yes and No. There are many different interpretations of this pattern. I like to have the methods in the Object, I'd say it belongs to the model. A simple example would be a sudoku solver that shows the solving steps in a GUI. Let's split it into some parts that can be separated into M, V and C: the raw data (2D array maybe), the sudoku functions (calc next step, ...), the GUI, someone who tells the GUI that a new step was calculated I'd put it like this: M: raw data + sudoku functions, C: who tells the GUI about changes / the model about GUI inputs, V: GUI without any logic others put the sudoku function into the Controller, is also right and might work better for some problems
It's possible to have a "one way" controller like you call it and the View is an Observer of the model It's also possible to let the Controller do everything and Model and View don't know each other (have a look at Model View Presenter, that's about that)
ReferenceURL : https://stackoverflow.com/questions/9674027/implementing-standard-software-design-patterns-focus-on-mvc-in-r
'your programing' 카테고리의 다른 글
원격 큐에 대한 클라이언트 JBoss 연결을 재활용 한 후에도 SpyJMSExceptions가 계속 발생하는 이유는 무엇입니까? (0) | 2020.12.31 |
---|---|
dev.hold, dev.flush 및 창 크기 조정 (0) | 2020.12.31 |
웹 애플리케이션없이 Amazon Elastic Beanstalk로 .NET Windows 서비스 배포 (0) | 2020.12.31 |
왜`std :: initializer_list`가 리터럴 유형으로 정의되지 않습니까? (0) | 2020.12.31 |
CoordinatorLayout에서 하단 막대 수정 (0) | 2020.12.31 |