2017-08-02 20:37:35
方面 | 面向过程 (以事件过程为中心) | 面向对象 (以对象为中心) |
---|---|---|
特点 |
|
|
优点 |
|
|
缺点 |
|
|
举例 |
|
|
汽车
高内聚,低耦合。High cohesion and low couping.
函数(对象, 参数列表)
summary
, plot
)根据对象的类,自动调用适用的方法对象.方法(参数列表)
pryr::otype
和pryr::ftype
判定对象/函数属于哪个OO系统在S3中,创建和初始化可以同时完成
library(pryr) car1 <- structure(list(manuf="VW", color="white", brand="Golf"), class="Car") otype(car1)
## [1] "S3"
str(car1)
## List of 3 ## $ manuf: chr "VW" ## $ color: chr "white" ## $ brand: chr "Golf" ## - attr(*, "class")= chr "Car"
或
car1 <- list(manuf="VW", color="white", brand="Golf") class(car1) <- "Car"
car2 <- structure(list(manuf="VW", color="red", brand="Audi"), class=c("Auto", "Car")) car3 <- structure(list(manuf="VW", color="black", brand="Santana"), class=c("Manual", "Car")) bike1 <- structure(list(manuf="Yongjiu", color="yellow", brand="ofo"), class="Bike") ## 是否属于Car类? sapply(list(car2, car3, bike1), inherits, what="Car")
## [1] TRUE TRUE FALSE
## 泛型函数shiftGear,采取"shiftGear"方法 shiftGear <- function(x, from, to, ...) { ## 校验数据结构 stopifnot(all(c("manuf", "color", "brand") %in% names(x))) ## 调用shiftGear方法 UseMethod("shiftGear") } ftype(shiftGear)
## [1] "s3" "generic"
shiftGear.Manual <- function(x, from, to, ...) { if (from == to) { ## 针对Manual类对象,定义专用的shiftGear方法 print(paste(class(x)[1], "类: 不需要换档")) }else{ print(paste(class(x)[1], "类:", x$color, x$manuf, x$brand, "踩离合,再从", from, "档隔级换到", to)) } } shiftGear.Car <- function(x, from, to, ...) { ## Car类的通用shiftGear方法 paste("我不知道怎么给", class(x)[1], "类", x$color, x$manuf, x$brand, "换档") } shiftGear.default <- function(x, ...) paste(class(x)[1], "类: 无法换档") ## 默认的shiftGear方法 methods(shiftGear)
## [1] shiftGear.Car shiftGear.default shiftGear.Manual ## see '?methods' for accessing help and source code
shiftGear(car2, 2, 4)
## [1] "我不知道怎么给 Auto 类 red VW Audi 换档"
shiftGear(car3, 2, 2)
## [1] "Manual 类: 不需要换档"
shiftGear(car3, 5, 2)
## [1] "Manual 类: black VW Santana 踩离合,再从 5 档隔级换到 2"
shiftGear(bike1, 1, 2)
## [1] "Bike 类: 无法换档"
notACar <- list(manuf="Nobody", color="opaque", brand="Nobrand") class(notACar) <- c("NotACar", class(car2)) shiftGear(notACar)
## [1] "我不知道怎么给 NotACar 类 opaque Nobody Nobrand 换档"
magrittr::%>%
x %>% f(y) ## 等价于 f(x, y)
library(magrittr) car3 %>% shiftGear(4, 2)
## [1] "Manual 类: black VW Santana 踩离合,再从 4 档隔级换到 2"
将方法定义在类内部 (建议用RC)
move <- function(car, speed, ...) UseMethod("move") move.Car <- function(car, speed, ...) print(paste(car$color, car$manuf, car$brand, "时速", speed, "行驶")) car4 <- structure(list(manuf="Toyota", color="white", brand="Accord", move=function(speed, ...) move(car4, speed, ...)), class=c("Auto", "Car")) car4$move(40)
## [1] "white Toyota Accord 时速 40 行驶"
计算右表图形的面积
部分计算公式重温如下:
shape | edge | neighbor | angle | |
---|---|---|---|---|
1 | 三角形 | 10 | 20 | 45 |
2 | 平行四边形 | 10 | 20 | 60 |
3 | 矩形 | 10 | 20 | NA |
4 | 菱形 | 10 | NA | 45 |
5 | 正方形 | 10 | NA | NA |
6 | 等边三角形 | 10 | NA | NA |
areas <- c(with(shapes[1,], edge * neighbor * sin(pi*angle/180) / 2), with(shapes[2,], edge * neighbor * sin(pi*angle/180)), with(shapes[3,], edge * neighbor), with(shapes[4,], edge ^ 2 * sin(pi*angle/180)), with(shapes[5,], edge ^ 2), with(shapes[6,], edge ^ 2 * sin(pi*60/180)) / 2) areas
## [1] 70.71068 173.20508 200.00000 70.71068 100.00000 43.30127
toNum <- function(shape, ...){ shape$neighbor <- as.numeric(shape$neighbor) shape$edge <- as.numeric(shape$edge) shape$angle <- as.numeric(shape$angle) return(shape) }
calcArea <- function(shape, ...) UseMethod("calcArea") ## 泛型函数 calcArea.Triangle <- function(shape, ...) ## 三角形通用公式 with(toNum(shape), edge * neighbor * abs(sin(pi * angle / 180)) / 2) calcArea.Parallelogram <- function(shape, ...) ## 平四通用公式 with(toNum(shape), edge * neighbor * abs(sin(pi * angle / 180))) calcArea.default <- function(shape, ...) NA ## 默认不计算面积
ifNA <- function(x, y) if (is.na(x)) y else x
imputeNeighbor <- function(shape, ...) ## 泛型函数 UseMethod("imputeNeighbor") imputeNeighbor.default <- function(shape, ...){ shape$neighbor <- with( shape, ifNA(neighbor, edge)) return(shape) }
imputeAngle <- function(shape, ...) ## 泛型函数 UseMethod("imputeAngle") imputeAngle.RegularTriangle <- function(shape, ...) { shape$angle <- ifNA(shape$angle, 60) return(shape) } imputeAngle.Square <- function(shape, ...) { shape$angle <- ifNA(shape$angle, 90) return(shape) } imputeAngle.ExactSquare <- imputeAngle.Square imputeAngle.default <- function(shape, ...) return(shape)
newshapes <- shapes newshapes$shapeclass <- c( "Triangle", rep("Parallelogram", 4), "Triangle") newshapes$shape <- c( "Triangle", "Parallelogram", "Square", "Rhombus", "ExactSquare", "RegularTriangle")
newshapes <- apply(newshapes, 1, function(x) structure(as.list(x), class=unname( x[c('shape', 'shapeclass')])))
str(newshapes)
List of 6 $ :List of 5 ..$ shape : chr "Triangle" ..$ edge : chr "10" ..$ neighbor : chr "20" ..$ angle : chr "45" ..$ shapeclass: chr "Triangle" ..- attr(*, "class")= chr [1:2] "Triangle" "Triangle" $ :List of 5 ..$ shape : chr "Parallelogram" ..$ edge : chr "10" ..$ neighbor : chr "20" ..$ angle : chr "60" ..$ shapeclass: chr "Parallelogram" ..- attr(*, "class")= chr [1:2] "Parallelogram" "Parallelogram" $ :List of 5 ..$ shape : chr "Square" ..$ edge : chr "10" ..$ neighbor : chr "20" ..$ angle : chr NA ..$ shapeclass: chr "Parallelogram" ..- attr(*, "class")= chr [1:2] "Square" "Parallelogram" ...
newareas <- newshapes %>% lapply(imputeNeighbor) %>% lapply(imputeAngle) %>% sapply(calcArea)
rbind(legacySolution=areas, oopSolution=newareas)
## [,1] [,2] [,3] [,4] [,5] [,6] ## legacySolution 70.71068 173.2051 200 70.71068 100 43.30127 ## oopSolution 70.71068 173.2051 200 70.71068 100 43.30127
Thank you!