gpt4 book ai didi

r - 在 R 中实现标准软件设计模式(重点是 MVC)

转载 作者:行者123 更新时间:2023-12-03 05:50:30 27 4
gpt4 key购买 nike

目前,我正在阅读很多有关软件工程、软件设计、设计模式等的内容。来自完全不同的背景,这些对我来说都是新的、令人着迷的东西,所以请耐心等待,以防我不使用正确的技术术语来描述某些方面;-)

我最终使用了Reference Classes (R 中 OOP 的一种方式)大多数时候是因为面向对象似乎是我正在做的很多事情的正确选择。

现在,我想知道是否有人在实现 MVC 方面有一些好的建议或经验。 ( Model View Controller ;也称为 MVP:模型 View 呈现器)R 中的模式,最好使用引用类。

我也对有关其他“标准”设计模式的信息非常感兴趣,例如 observer , blackboard等等,但我不想让这个问题变得太宽泛。我想最酷的事情就是看到一些最小的示例代码,但是任何指针、“模式”、图表或任何其他想法也将受到极大的赞赏!

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

  1. The Pragmatic Programmer
  2. Design Patterns

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

类定义 Controller 和 View

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 已在初始化时自动注册到观察者中。这样, Controller / View 就可以订阅通知,并且我们有 1:n 的关系。

实例化 View 和 Controller

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

订阅

Controller 订阅底层模型的通知

cont$subscribe()

请注意,订阅已记录在观察者中

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

显示注册 View

> 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

请注意,当底层模型将其状态更改发布给观察者时,两个注册 View 都会自动更新,观察者又会通知所有订阅者(即 Controller )。

开放问题

我觉得我还没有完全理解:

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

最佳答案

  1. 它看起来相当不错,但我不太确定为什么除了其他类之外还有一个观察者(也许你可以告诉我)通常 Controller 是一个观察者。在 R 中执行此操作确实是个好主意,因为当我在 Java 中学习它时,它并不那么容易理解(Java 隐藏了一些好的部分)

  2. 是和否。对此模式有许多不同的解释。我喜欢在对象中拥有方法,我想说它属于模型。一个简单的例子是数独求解器,它在 GUI 中显示求解步骤。让我们把它分成一些可以分成 M、V 和 C 的部分:原始数据(可能是二维数组)、数独函数(下一步计算,...)、GUI、告诉 GUI 一个新的人计算了步长我会这样说:M:原始数据+数独函数,C:谁告诉GUI有关更改/有关GUI输入的模型,V:没有任何逻辑的GUI其他人将数独功能放入Controller中,也是对的,并且可能会更好地解决某些问题

  3. 可以有一个像您所说的“单向” Controller ,并且 View 是模型的观察者也可以让 Controller 做所有事情,而 Model 和 View 彼此不认识(看看 Model View Presenter,就是这样)

关于r - 在 R 中实现标准软件设计模式(重点是 MVC),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9674027/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com