Rの標準ソフトウェア設計パターンの実装(MVCに焦点を当てて)
-
12-12-2019 - |
質問
現在、ソフトウェア工学、ソフトウェア設計、デザインパターンなどを読んでいます。まったく異なる背景から来て、それは私にとってすべての新しい魅力的なものですので、私が使用していない場合は私と一緒にいるのです。特定の側面を説明するための正しい技術用語。 - )
私はリファレンスクラス(rのOOPの方法)私がやっていることの多くのもののための正しい選択であるようです。
今、私は誰かがいくつかの良いアドバイスを持っているかどうか、あるいは MVC (モデルビューコントローラ; MVP :モデルビュー発表者としても知られています。 。
オブザーバ、黒板など。これを幅広い質問にしたくない。私は最もクールなことがいくつかの最小の例のコードを見ることになるだろうと思いますが、「スキーマ」、図、その他の考え方も大幅に感謝されるでしょう!
似たようなものに興味がある人のために、私は本当に以下の本をお勧めします:
- Pragmatic Programmer
- REL="NOREFERRER">デザインパターン
- これはMVCパターンのやや正しい実装ですか?そうでなければ、私は何を間違っていましたか?
- モデル「属する」モデルまたはコントローラクラスのメソッドの処理方法(例えば、データ、サブセットなど)を「集計する」必要があります。これまでのところ、私は常に特定のオブジェクトがこの非常にオブジェクトのメソッドとして "do"できることをすべて定義しました。
- コントローラは、モデルとビューの間のすべての対話を制御する「プロキシ」の種類のものであるべきです(「両方の方法」)、またはモデルへのユーザー入力を伝播するのは責任を負います(「片道」の種類?< / li>
アップデート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がオブザーバに自動的に登録されていることに注意してください。そのように、コントローラ/ビューは通知を購読することができ、私たちは1:n関係を持っています。
ビューとコントローラのインスタンス化
view1 <- new("View1")
view2 <- new("View2")
cont <- new("Controller", model=mod, views=list(view1, view2))
.
購読
コントローラは、基礎となるモデルの通知を購読する
cont$subscribe()
.
購読がオブザーバ
に記録されていることに注意してください。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
.
基礎となるモデルがオブザーバへの状態変化を公開するため、両方の登録ビューは自動的に更新され、それはすべての加入者(すなわちコントローラ)を通知する。
OPENQUS
これは私がまだ完全に理解していないような気がしていることです:
解決
-
それは非常によく見えますが、あなたが他のクラスに追加のオブザーバを持っているのかどうかは確かではありません(多分あなたは私に言うことができる)通常コントローラはオブザーバーです。私がJavaでそれを学んだときにそれほど簡単ではなかった(Javaが良い部分のいくつかを隠す)
の中でこれをすることは本当に良い考えです(Javaは良い部分のいくつかを隠す)
-
はい、いいえ。このパターンのさまざまな解釈がさまざまです。私はオブジェクトのメソッドを持っているのが好きです、私はそれがモデルに属していると言うでしょう。 簡単な例は、GUIの解決ステップを示す数独ソルバーです。 M、V、C:生データ(2Dアレイ)、数独機能(Calc Next Step、...)、GUI、GUI、GUIに新しいことを言う人、GUI、GUIステップが計算されました 私はこのようにそれを置くでしょう:M:生データ+数独機能、C:GUIの変更/モデルについてGUIをGUIに伝え、論理なしのGUI 他の人はSudoku機能をコントローラに入れています、そして正しい、いくつかの問題に対してよりよく機能するかもしれません
-
あなたがそれを呼び出して、ビューはモデルのオブザーバになるような "1つの方法"コントローラを持つことが可能です コントローラにすべてをやり直すこともできます(モデルビュープレゼンターを見てください、それはそれについて)
を知らないようにすることも可能です。