学习笔记,仅供参考,有错必纠
参考自:《R的极客理想》-- 张丹
R语言中有三种面向对象的编程实现,即S3类型、S4类型和RC类型。S3和S4都是基于泛型函数的,RC则是完全的面向对象实现。
定义老师对象和行为,封装到teacher()泛型函数中;
定义同学对象和行为,封装到student()泛型函数中.
#定义老师对象和行为
teacher <- function(x, ...) UseMethod("teacher")
teacher.lecture <- function(x) print("讲课")
teacher.assignment <- function(x) print("布置作业")
teacher.correcting <- function(x) print("批改作业")
teacher.default<-function(x) print("你不是teacher")
#定义同学对象和行为
student <- function(x, ...) UseMethod("student")
student.attend <- function(x) print("听课")
student.homework <- function(x) print("写作业")
student.exam <- function(x) print("考试")
student.default<-function(x) print("你不是student")
#定义变量a和b
a <- "teacher"
b <- "student"
attr(a, "class") <- "lecture"
attr(b, "class") <- "attend"
teacher(a) #"讲课"
student(b) #"听课"
#把老师和同学的行为都赋予到一个变量ab上
ab <- "teacher_student"
#分别设置不同对象的行为
attr(ab, "class") <- c("assignment", "homework")
#执行老师的行为
teacher(ab) #"布置作业"
#执行同学的行为
student(ab) #"写作业"
在R语言中,基于S3对象的面向对象编程,是一种基于泛型函数的实现方式。泛型函数是一种特殊的函数,根据传入对象的类型决定调用哪个具体的方法。基于S3对象实现的面向对象编程,不同于其他语言的面向对象编程,是一种动态函数调用的模拟实现。。S3对象被广泛应用于R的早期的开发包中。
创建S3对象最简单的方法就是给一个变量增加class属性:
x <- 1
#定义为S3类型对象
attr(x, "class") <- "foo"
x
#输出start
[1] 1
attr(,"class")
[1] "foo"
#输出end
otype(x) #"S3"
也可以通过structure函数创建S3对象:
#创建S3类型对象
y <- structure(10, class = "foo")
y
#输出start
[1] 10
attr(,"class")
[1] "foo"
#输出end
otype(y) #"S3"
S3对象没有明确的结构关系,一个S3对象可以有多个类型。S3对象类型,通过变量的class属性来定义,class属性可以是一个向量,所以允许多类型:
z <- 5
attr(z, "class") <- c("foo", "bar")
class(z) #"foo" "bar"
otype(z) #"S3"
对于S3对象的使用,通常用UseMethod函数来定义一个泛型函数的名称,通过传入参数的class属性来确定不同的方法调用:
#用UseMethod定义teacher泛型函数
teacher <- function(x, ...) UseMethod("teacher")
#函数检查teacher的类型
ftype(teach)
#用teacher.xxx的语法格式定义teacher对象的行为
teacher.lecture <- function(x) print("讲课")
teacher.correcting <- function(x) print("批改作业")
#teacher.default定义默认行为
teacher.default<-function(x) print("teacher默认行为")
方法调用时,通过传入参数的class属性来确定不同的方法调用,比如,我们定义一个变量a,并设置a的class属性为lecture,将变量a传入teacher泛型函数中,teacher.lecture函数就会被调用:
a <- "teacherA"
attr(a, "class") <- "lecture"
#执行老师的行为
teacher(a)
当然,我们也可以直接调用teacher中定义的行为,如果这样做就失去了面向对象封装的意义:
teacher.lecture() #"讲课"
teacher.lecture(a) #"讲课"
teacher() #"teacher默认行为"
对比其他语言,这里R的泛型函数就表示方法接口,teacher.xxx表示接口的方法实现。
当我们使用S3对象进行面向对象封装后,可以用methods函数来查看S3对象中定义的内部行为函数:
#查看teacher对象
teacher
#输出start
function(x, ...) UseMethod("teacher")
#输出end
#查看teacher对象的内部函数
methods(teacher)
#输出start
teacher.assignment teacher.correcting teacher.default teacher.lecture
#输出end
用getAnywhere函数查看函数:
#查看teacher.lecture
getAnywhere(teacher.lecture)
#输出start
A single object matching ‘teacher.lecture’ was found
It was found in the following places
.GlobalEnv
registered S3 method for teacher
with value
function(x) print("讲课")
#输出end
#查看不可见的函数predict.ppr
getAnywhere(predict.ppr)
#输出略
使用getS3method函数查看不可见的函数:
getS3method("predict", "ppr")
#输出略
S3对象可以用NextMethod函数来实现继承调用:
node <- function(x) UseMethod("node", x)
node.default <- function(x) print("默认node")
node.father <- function(x) c("father") #father函数
node.son <- function(x) c("son", NextMethod()) #son函数
n1 <- structure(1, class = c("father"))
n2 <- structure(2, class = c("son", "father"))
node(n1) #"father"
node(n2) #"son" "father"
通过对node函数传入n2的参数,node.son先被执行,然后通过NextMethod函数继续执行node.father函数。其实这样就模拟了子函数调用父函数的过程,实现面向对象编程中的继承。
S3对象并不是完全的面向对象实现,而是一种通过函数调用来模拟面向对象实现.
S3使用起来简单,但在实际的面向对象编程过程中,当对象关系有一定的复杂度时,S3对象所表达的意义就会变得不太清楚.
S3封装的内部函数可绕过泛型函数的检查直接被调用.
S3参数的class属性可以被任意设置,没有预处理的检查.
S3参数只能通过调用class属性进行函数调用,其他属性则不会被class函数执行.
S3参数的class属性有多个值时,调用时会按照程序赋值顺序来调用第一个合法的函数.