在R中实现标准的软件devise模式(专注于MVC)

目前,我正在阅读很多有关软件工程,软件devise,devise模式等方面的内容。从完全不同的背景来看,这些对我来说都是新奇的东西,所以如果我不使用正确的技术术语,请耐心等待描述某些方面;-)

在大多数情况下,我最终都使用了引用类 (在R中是一种OOP方法),因为面向对象似乎是我正在做的很多事情的正确select。

现在,我想知道是否有人在R中实现MVC (Model View Controller;也称为MVP :Model View Presenter)模式方面有一些很好的build议或经验,最好使用Reference Classes。

我也会对其他“标准”devise模式(如观察者 , 黑板等)的信息非常感兴趣,但是我不想把这个问题做得太宽泛。 我想最酷的是看到一些最小的示例代码,但任何指针,“架构”,图表或任何其他的想法也将不胜感激!

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

  1. 语用程序员
  2. devise模式

更新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) } ) ) 

创build实例

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

请注意,订阅已被logging在观察者中

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

显示注册视图

 > cont$updateView() ab 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 ab 1 10 100 2 20 110 3 30 120 [1] TRUE 

请注意,两个已注册的视图都会自动更新,因为底层模型会将其状态更改发布给观察者,观察者会通知所有用户(即控制器)。

打开问题

以下是我现在还不完全了解的内容:

  1. 这是MVC模式的一个有点正确的实现吗? 如果没有,我做错了什么?
  2. 模型的“处理”方法(例如聚合数据,子集等)是否属于模型或控制器类。 到目前为止,我总是定义一个特定的对象可以“做”作为这个对象的方法的一切。
  3. 控制器应该是控制模型和视图之间每种交互(“双向”)的“代理”,还是仅仅负责将用户input传播给模型(“单向”的types?
  1. 它看起来相当不错,但我不确定为什么你有一个观察者额外的其他类(也许你可以告诉我)通常控制器是一个观察者。 在R中这样做是一个非常好的主意,因为当我在Java中学习它时,并不容易理解(Java隐藏了一些好的部分)

  2. 是的,没有。这种模式有很多不同的解释。 我喜欢在对象中的方法,我说它属于模型。 一个简单的例子是一个数独求解器,它显示了GUI中的求解步骤。 让我们把它分成几部分,可以分为M,V和C:原始数据(可能是二维数组),数独function(下一步计算,…),GUI,告诉GUI一个新的步骤是这样计算的:M:原始数据+数独函数,C:谁告诉GUI有关GUIinput的改变/模型,V:没有任何逻辑的GUI,其他人把数独function放到Controller中,是也是正确的,可能会更好地解决一些问题

  3. 可以有一个“单向”控制器,就像你调用它,而视图是模型的观察者。也可以让控制器做所有事情,模型和视图彼此不了解(看看Model View Presenter就是这样)

Interesting Posts