Factoryパターン
CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 43/57。
ファクトリとは、呼び出し元がクラスやそのコンストラクターを知る必要がないように、オブジェクトを作成する関数です。型名などの説明を受け取り、適切なオブジェクトを返します。
area <- function(s) UseMethod("area")
area.circle <- function(s) pi * s$r^2
area.square <- function(s) s$side^2
make_shape <- function(type, size) {
switch(type,
circle = structure(list(r = size), class = "circle"),
square = structure(list(side = size), class = "square"))
}
for (t in c("circle", "square")) cat(t, round(area(make_shape(t, 2)), 2), "\n")出力:
circle 12.57
square 4 認識できない型の場合、switch()はNULLを返します。ファクトリは代わりに型の名前を示すメッセージを表示して停止すべきです。そうすれば、間違いが発生した場所で明らかになります。
make_shape <- function(type, size) {
switch(type,
circle = structure(list(r = size), class = "circle"),
square = structure(list(side = size), class = "square"),
stop("unknown shape: ", type, call. = FALSE))
}
msg <- tryCatch(make_shape("hexagon", 1), error = function(e) conditionMessage(e))
cat(msg)出力:
unknown shape: hexagonレジストリはコンストラクターを名前付きリストに保持するため、ファクトリ自体を変更する必要がありません。ここではレジストリは環境であり、プログラムの実行中にregister_shape()が新しい種類を追加します:
registry <- new.env()
register_shape <- function(name, maker) assign(name, maker, envir = registry)
make_shape <- function(name, ...) {
if (!exists(name, envir = registry, inherits = FALSE)) stop("unknown shape: ", name, call. = FALSE)
get(name, envir = registry)(...)
}
register_shape("circle", function(r) structure(list(r = r), class = "circle"))
register_shape("rect", function(w, h) structure(list(w = w, h = h), class = "rect"))
print(class(make_shape("rect", 2, 3)))
print(sort(ls(registry)))出力:
[1] "rect"
[1] "circle" "rect" オブジェクトを使用するコードは、area()のようなジェネリックにのみ依存します。登録され、area()メソッドを持つ新しいクラスは、そのコードを一切変更せずに動作します:
registry <- new.env()
register_shape <- function(name, maker) assign(name, maker, envir = registry)
make_shape <- function(name, ...) get(name, envir = registry)(...)
area <- function(s) UseMethod("area")
total_area <- function(shapes) sum(vapply(shapes, area, numeric(1)))
register_shape("square", function(a) structure(list(a = a), class = "square"))
area.square <- function(s) s$a^2
register_shape("tri", function(b, h) structure(list(b = b, h = h), class = "tri"))
area.tri <- function(s) s$b * s$h / 2
print(total_area(list(make_shape("square", 2), make_shape("tri", 4, 3))))出力:
[1] 10チャレンジ
簡単レコードは複数の形式でエクスポートできます。Exporters.Rには、汎用のexport(exporter, record)と、値をカンマで結合して返すcsv_exporterのメソッドが含まれています。そこにある残り2つのメソッドを完成させてください:
json_exporter:{"name": "Ada", "age": "36"};text_exporter:name=Ada; age=36。
次に、Factory.Rでファクトリmake_exporter(format)を作成してください:csv、json、textの場合は、それぞれクラスcsv_exporter、json_exporter、text_exporterを持つ空のリストを返し、それ以外の形式の場合はunknown format: xmlで停止します(call. = FALSEを使用してください)。提供されているコードはフィールド名と値を読み取り、続いて形式名を読み取り、各エクスポート結果、またはerror: とそのメッセージを出力します。
コードはExporters.RとFactory.Rに記述してください。main.Rには提供された入出力コードが含まれており、編集できません。
自分で試してみよう
source("Exporters.R")
source("Factory.R")
# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
fields <- strsplit(input[1], ",")[[1]]
values <- strsplit(input[2], ",")[[1]]
record <- setNames(values, fields)
for (fmt in input[-(1:2)]) {
out <- tryCatch(export(make_exporter(fmt), record), error = function(e) paste("error:", conditionMessage(e)))
cat(out, "\n", sep = "")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rオンラインコンパイラ