目前,我正在阅读大量关于软件工程、软件设计、设计模式等的书籍.这些书籍来自完全不同的背景,对我来说都是全新的、有趣的东西,因此,如果我没有使用正确的技术术语来描述某些方面,请耐心听我说;-)

我大部分时间都使用Reference Classes(R中的OOP方式),因为对于我正在做的很多事情来说,面向对象似乎是正确的 Select .

现在,我想知道是否有人对在R中实现100(Model View Controller;也称为MVP:Model View Presenter)模式(最好使用参考类)有一些好的建议或经验.

我还对其他"标准"设计模式(如observerblackboard等)的信息非常感兴趣,但我不想让这个问题过于宽泛.我想最酷的事情是看到一些最小的示例代码,但任何指针、"模式"、图表或任何其他 idea 也将非常感谢!

对于那些对类似东西感兴趣的人,我真的可以推荐以下几本书:

  1. The Pragmatic Programmer
  2. Design Patterns

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

研究模型特性和观测器状态

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在初始化时已自动注册到observer中.这样,控制器/视图就可以订阅通知,我们之间的关系是1:n.

实例化视图和控制器

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

订阅

控制器订阅基础模型的通知

cont$subscribe()

请注意,订阅已登录到observer

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

请注意,当基础模型向观察者发布其状态更改时,两个注册的视图都会自动更新,观察者会通知所有订阅者(即控制器).

开放性问题

以下是我尚未完全理解的感受:

  1. 这是MVC模式的正确实现吗?如果不是,我做错了什么?
  2. 模型的"处理"方法(如聚合数据、获取子集等)是否属于模型或控制器类.到目前为止,我总是将特定对象可以"做"的所有事情定义为这个对象的方法.
  3. 控制器应该是一种"代理",控制模型和视图之间的每一次交互(有点"双向"),还是只负责向模型传播用户输入(有点"单向"?

推荐答案

  1. 它看起来很不错,但我不太确定为什么在其他类之外还有一个观察器(也许你可以告诉我),通常控制器是观察器.在R中这样做是一个非常好的主意,因为当我在Java中学习它时,它并不那么容易理解(Java隐藏了一些好的部分)

  2. 是和否.对这种模式有很多不同的解释.我喜欢对象中的方法,我认为它属于模型.

  3. 有可能有一个像你所说的"单向"控制器,视图是模型的观察者

R相关问答推荐

for循环和if else在R中

如果R上的不同时期之间的值发生了变化,则创建假人

是否可以通过另一个DF的内容过滤数据帧列表?

R根据名称的载体对收件箱列采取行动

是什么导致R中的mvtnorm包中出现这个错误?

pdf Quarto中的中心美人鱼

如何使用TukeyHSD绘制事后概率热图

通过R访问MoveApps API

如何使用文本表达来子集数据

Select 与特定列中最大值对应的数据帧行

将模拟变量乘以多个观测结果中的模拟变量

在不安装软件包的情况下测试更新

在R中替换函数中的特定符号

如何使用按钮切换轨迹?

如何在Chart_Series()中更改轴值的 colored颜色 ?

Ggplot2中的重复注记

R spatstat Minkowski Sum()返回多个边界

来自程序包AFEX和amp;的类/函数和NICE_TABLE&冲突

为什么这个表格格罗布不打印?

计算Mean by分组和绑定到R中的数据集