R语言如何面向对象编程—s4类

1 了解R语言类

面向对象是一种对现实世界理解和抽象的方法,当代码复杂度增加难以维护的时候,面向对象就会显得非常重要。R主要面向统计计算,而且代码量一般不会很大,几十行,几百行,使用面向过程的编程方法就可以很好地完成编程的任务。
不过,伴随着越来越多的工程背景的人的加入,R语言开始向更多的领域发展。原来的少量的代码的面向过程的编码方式,会越来越难以维护海量代码的项目,所以必须有一种新的编程方式来代码原来的面向过程的编码思路,这种新的编程方式就是面向对象编程(Object Oriented Programming, OOP)。
R语言的类有S3类和S4类,S3类用的比较广,创建简单粗糙但是灵活,而S4类比较精细,具有严格的类结构,从其他语言过来的用户可能会习惯一些。S4对象系统具有明显的结构化特征,更适合面向对象的程序设计。Bioconductor社区,以S4对象系统做为基础架构,只接受符合S4定义的R包。

2 S4类创建—setClass()

由于S4对象是标准的面向对象实现方式, 有专门的类定义 setClass() 和类的实例化函数new()。 setClass函数声明如下:

setClass(Class, slots ,prototype, contains=character(), validity, sealed)

各参数含义:

Class 定义的类名称
slots 定义属性与类型, 参数为list
prototype 属性的默认值
contains 继承的父类
validity 定义属性的类型检查
sealed 如果设置TRUE,则同名类不能被再次定义
#定义一个 人 类
setClass(
  "person", 
  slots=list(name="character", age="numeric"),
  prototype = list(name="nobody", age=20)
)
# new()实例化一个人 
zhangsan <- new(Class ="person", name="zhangsan", age = 26)
# 查看 zhangsan的属性 
zhangsan
An object of class "person"
Slot "name":
[1] "zhangsan"

Slot "age":
[1] 26
# zhangsan 属于person类
class(zhangsan)
[1] "person"
attr(,"package")
[1] ".GlobalEnv"
#另外,S4中我们只能使用@来访问一个对象的属性 
zhangsan@name
> [1] "zhangsan"

3 S4类的类型检查—setValidity()

s4类中包括强制类型检查, 如果初始化类型与定义类型不同无法通过,如

# name 传入的不是字符串
lisi <- new(Class ="person", name=11, age = 22)
Error in validObject(.Object) : 
  类别为“person”的对象不对: invalid object for slot "name" in class "person": got class "numeric", should be or extend class "character"

也可以使用setValidity()函数设定额外的正当性检查 , 比如年龄不可以为负数,

setValidity("person", function(object){
    
    
  if (object@age <= 0) stop("Age is negative")
})
# age 传入负数
> lisi <- new(Class ="person", name="lisi", age = -1)
 Error in validityMethod(object) : Age is negative 

4 S4类的泛型函数

泛型函数通俗的就是同一个函数名, 遇到不同的对象产生不同的效果。
S4的泛型函数通过**setGeneric()**来定义泛型接口,通过setMethod()来定义现实类。这样可以让S4对象系统,更符合面向对象的特征。下面的例子说明了什么泛型函数

# 设定一个函数来 自我介绍 
# 普通函数 
selfIntroduce<- function(name){
    
    
  sprintf("my name is %s", name)
}
selfIntroduce("Sun wukong")
>"my name is Sun wukong" 
# 将函数定义为泛型函数,即接口
setGeneric("selfIntroduce",
           function(object){
    
    
             standardGeneric("selfIntroduce")
          }
)
selfIntroduce("Sun wukong")# 这时普通函数已经不能使用了, 泛型函数必须指定具体类的方法
#Error in (function (classes, fdef, mtable)  : 
# unable to find an inherited method for function ‘selfIntroduce’ for signature #‘"character"’ 

# 定义函数的现实,并指定参数类型为person对象时如何运行
setMethod("selfIntroduce", 
          signature(object = "person"),
          function(object){
    
    
            sprintf("my name is %s, i'm %d years old", object@name, object@age)
          }
)
selfIntroduce(zhangsan)
>"my name is zhangsan, i'm 26 years old"
# 增加一个函数的现实,指定参数类型为character对象时如何运行
setMethod("selfIntroduce", 
          signature(object = "character"),
          function(object){
    
    
            sprintf("my name is %s", object)
          }
)
selfIntroduce("Sun wukong")
>"my name is Sun wukong" 
# 查看类函数
> showMethods(selfIntroduce)
Function: selfIntroduce (package .GlobalEnv)
object="character"
object="Person"

# 查看类函数定义
getMethod("selfIntroduce", "Person")
Method Definition:

function (object) 
{
    
    
    sprintf("my name is %s, i'm %d years old", object@name, 
        object@age)
}

Signatures:
        object  
target  "Person"
defined "Person"

可以看到泛型就是遇到同的对象使用不同的方法

5 S4类的继承

继承一个类使用setClass()中参数 contains指定父类

#新建 student 类, 继承 person 类。 
setClass(Class = "student",
         slots=list( school="character", major="character", father="person"),
         contains="person")
xiaoming<- new(Class ="student", 
			    name = "xiaoming", 
			    age = 18, 
			    school="qinghuauni", 
			    major = "computer science",
			    father = zhangsan
		   )

猜你喜欢

转载自blog.csdn.net/weixin_43705953/article/details/115348720