your programing

R에서 표준 소프트웨어 디자인 패턴 구현 (MVC에 중점)

lovepro 2020. 12. 31. 23:07
반응형

R에서 표준 소프트웨어 디자인 패턴 구현 (MVC에 중점)


현재 저는 소프트웨어 엔지니어링, 소프트웨어 디자인, 디자인 패턴 등에 대해 많이 읽고 있습니다. 완전히 다른 배경에서 온 것은 저에게 완전히 새로운 흥미로운 내용입니다. 올바른 기술 용어를 사용하지 않는 경우를 대비해 주시기 바랍니다. 특정 측면을 설명하기 위해 ;-)

내가 사용하는 결국 참조 클래스 객체 지향은 내가 뭘하는 물건을 많이에 대한 올바른 선택이 될 것 때문에 (R의 OOP의 방법) 대부분의 시간을.

이제 R 에서 MVC (Model View Controller; MVP : Model View Presenter 라고도 함 ) 패턴 을 구현하는 것과 관련하여 좋은 조언이나 경험이 있는지 궁금합니다 . 가급적 참조 클래스를 사용합니다.

관찰자 , 칠판 등과 같은 다른 "표준"디자인 패턴에 대한 정보에도 관심이 많지만이 질문을 너무 광범위하게 만들고 싶지는 않습니다. 가장 멋진 것은 최소한의 예제 코드를 보는 것입니다. 그러나 포인터, "스키마", 다이어그램 또는 기타 아이디어도 크게 감사 할 것입니다!

비슷한 내용에 관심이있는 분들을 위해 다음 책을 추천 할 수 있습니다.

  1. 실용적인 프로그래머
  2. 디자인 패턴

업데이트 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:

  1. Is this a somewhat correct implementation of the MVC pattern? If not, what did I do wrong?
  2. 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.
  3. 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"?

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

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

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

반응형