一、基于S3的面向对象编程
基于S3的面向对象编程是一种基于泛型函数(generic function)的实现方式。
1.S3函数的创建
S3对象组成:generic(generic FUN)+method(generic.class FUN)
泛型函数(generic)创建示例:
get_n_elements <- function(x,...)
{
UseMethod("get_n_elements")
}
通常用UseMethod()函数定义一个泛型函数的名称,通过传入参数的class属性,来确定对应的方法调用。
method(generic.class)函数,创建示例:
# Create a data.frame method for get_n_elements
get_n_elements.data.frame <- function(x, ...)
{
nrow(x) * ncol(x) # or prod(dim(x))
}
# Create a default method for get_n_elements
#在使用UseMethod调用时,先在methods中寻找对应class,如果都没有找到,则会调用#default方法。
get_n_elements.default <- function(x,...)
{
length(unlist(x))
}
methods() 用于查找S3泛型函数中所有可用的methods。
调用pryr包中的is_s3_method() 可以验证函数是否S3方法。
2、S3对象的传入参数有多个class属性的处理方法
当变量x具有多个class属性,应按具体到通用的顺序来排列变量对应的class。
使用NextMethod()来调用methods
an_s3_method.some_class <- function(x, ...)
{
# Act on some_class, then
NextMethod("an_s3_method")
}
具体示例如下:
# Assign classes
class(kitty) <- c("cat","mammal","character")
what_am_i <- function(x,...)
{
UseMethod("what_am_i")
}
# cat method
what_am_i.cat <- function(x)
{
message("I'm a cat")
NextMethod("what_am_i")
}
# mammal method
what_am_i.mammal <- function(x, ...)
{
message("I'm a mammal")
NextMethod("what_am_i")
}
# character method
what_am_i.character <- function(x, ...)
{
message("I'm a character vector")
}
二、基于R6的面向对象编程
1、R6对象的创建
首先使用 R6Class() 创建一个class generator(也可叫factory)。
第一个参数是创建的对象的类的名字。
参数private为一个list,为对象保存数据域(data field),包含每个元素的名字。
参数pubilc为一个list,为对象保存面向用户的函数或功能。
library(R6)
thing_factory <- R6Class(
"Thing",
private = list(
a_field = "a value",
another_field = 123
),
public = list(
do_something = function(x, y, z) {
# Do something here
}
)
)
创建factory后,再调用new()来生成一个R6对象。new()无需定义,所有的factory都默认具有该方法。
a_thing <- thing_factory$new()
initialize()是一种特殊的公有方法(public method),
在R6对象创建时自动调用,用来设置私域(private field)值。
new()中的参数被传给
initialize()。
# Add an initialize method
microwave_oven_factory <- R6Class(
"MicrowaveOven",
private = list(
power_rating_watts = 800,
door_is_open = FALSE
),
public = list(
cook = function(time_seconds) {
Sys.sleep(time_seconds)
print("Your food is cooked!")
},
open_door = function() {
private$door_is_open = TRUE
},
close_door = function() {
private$door_is_open = FALSE
},
# Add initialize() method here
initialize = function(power_rating_watts,door_is_open){
if(!missing(power_rating_watts)){
private$power_rating_watts<-power_rating_watts
}
if(!missing(door_is_open)){
private$door_is_open<-door_is_open
}
}
)
)
# Make a microwave
a_microwave_oven <- microwave_oven_factory$new(
power_rating_watts = 650,
door_is_open = TRUE
)
2.访问和设置私有域
private组件中的数据对用户隐藏,这是封装的原理。使用private$前缀可以访问私域(private field)。
存在active组件中的active binding(行为像变量的函数),可以获取和设置私有数据域。
Active bindings是R6中一种特殊的函数调用方式,把对函数的访问表现为对属性的访问,属于公有组件。
thing_factory <- R6Class(
"Thing",
private = list(
..a_field = "a value"
),
active = list(
a_field = function(value) {
if(missing(value)) {
private$..a_fi