Menu
CoddyTech

Mẫu Factory

Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 43 trên 57.

Factory là một hàm tạo đối tượng để bên gọi không cần biết lớp hoặc hàm khởi tạo của đối tượng đó. Hàm này nhận một mô tả, chẳng hạn như tên kiểu, rồi trả về đối tượng phù hợp:

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

Đầu ra:

circle 12.57 
square 4 

Với một kiểu mà nó không nhận biết, switch() trả về NULL. Thay vào đó, factory nên dừng và đưa ra thông báo nêu rõ kiểu đó, để lỗi hiển thị ngay tại nơi nó xảy ra:

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)

Đầu ra:

unknown shape: hexagon

Bộ đăng ký lưu các hàm khởi tạo trong một danh sách có tên, nên bản thân hàm factory không bao giờ thay đổi. Ở đây, bộ đăng ký là một môi trường, còn register_shape() thêm một loại mới khi chương trình đang chạy:

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

Đầu ra:

[1] "rect"
[1] "circle" "rect"  

Code sử dụng các đối tượng chỉ phụ thuộc vào những thành phần tổng quát, chẳng hạn như area(). Một lớp mới được đăng ký và có phương thức area() sẽ hoạt động với đoạn code đó mà không cần thay đổi gì:

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

Đầu ra:

[1] 10
challenge icon

Thử thách

Dễ

Một bản ghi có thể được xuất ra ở nhiều định dạng. Exporters.R chứa hàm tổng quát export(exporter, record) và phương thức dành cho csv_exporter, trả về các giá trị được nối bằng dấu phẩy. Hãy hoàn thành hai phương thức còn lại trong tệp đó:

  • json_exporter: {"name": "Ada", "age": "36"};
  • text_exporter: name=Ada; age=36.

Sau đó, viết hàm nhà máy make_exporter(format) trong Factory.R: csv, json hoặc text sẽ tạo ra một danh sách rỗng có lớp csv_exporter, json_exporter hoặc text_exporter, còn mọi định dạng khác sẽ dừng lại với thông báo unknown format: xml (dùng call. = FALSE). Code được cung cấp sẽ đọc tên các trường và giá trị, sau đó đọc tên định dạng, rồi in kết quả xuất của từng định dạng hoặc error: cùng thông báo lỗi.

Hãy viết code vào Exporters.R và Factory.R. main.R chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

source("Exporters.R")
source("Factory.R")

# Đoạn code đầu vào và đầu ra đã có sẵn: bạn giữ nguyên
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 = "")
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch R online