1、基於S3的面向對象編程數據庫
基於S3的面向對象編程是一種基於泛型函數(generic function)的實現方式。編程
1.S3函數的建立函數
S3對象組成:generic(generic FUN)+method(generic.class FUN)spa
泛型函數(generic)建立示例:code
get_n_elements <- function(x,...) { UseMethod("get_n_elements") }
一般用UseMethod()函數定義一個泛型函數的名稱,經過傳入參數的class屬性,來肯定對應的方法調用。對象
method(generic.class)函數,建立示例:blog
# 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方法。element
二、S3對象的傳入參數有多個class屬性的處理方法get
當變量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") }
2、基於R6的面向對象編程
一、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_field } else { assert_is_a_string(value) # or another assertion private$..a_field <- value } } ) )
將active binding做爲數據變量而不是函數進行調用。
a_thing <- thing_factory$new()
a_thing$a_field # not a_thing$a_field()
三、R6的繼承
(1)子類的建立
繼承將一個類(class)的功能複製到另外一個。可在R6Class()中用inherit參數來建立子類。
child_class_factory <- R6Class(
"ChildClass",
inherit = parent_class_factory
)
子類能夠添加公有方法來擴展父類的功能,並在公有方法中使用前綴self$調用
其餘公有方法。
子類也可定義與父類相同的方法名稱來重寫該方法,用於擴展父類的功能,子類使用前綴super$訪問
父類的公有方法。
(2)跨級訪問
R6類默認只能使用直接父類的功能。爲了跨級訪問,中間類(intermediate classes)首先須要定義一個active binding來顯示父類,形式以下:
active = list(
super_ = function() super
)
而後能夠跨級使用方法parent_method <- super$method()grand_parent_method <- super$super_$method(great_grand_parent_method <- super$super_$super_$method()
(3)共享域
大部分類型的R變量是經過值複製,意思是當複製一個變量時 ,新的變量具備本身的複製值,改變其中一個變量不會影響另一個。
環境變量(Environments)比較特殊,經過地址傳送來複制(by reference),所以全部的複製品都是等同的,改變其中一個就會改變其餘變量。R6類可利用環境的地址傳送(by reference)複製行爲在對象之間共享區域,定義一個名爲shared的私域,方式以下:
建立一個新的環境,指定該環境的任意共享域,可經過active bindings訪問。
工做方式與其餘active bindings相同,但需使用private$shared$前綴來找回(retrieve)這些區域
R6Class( "Thing", private = list( shared = { e <- new.env() e$a_shared_field <- 123 e } ), active = list( a_shared_field = function(value) { if(missing(value)) { private$shared$a_shared_field } else { private$shared$a_shared_field <- value } } ) )
(4)R6對象的複製
R6對象使用與環境變量相同的地址傳送複製方式,若是使用<- 符號複製R6對象,原對象的變化會影響複製品。
a_reference_copy <- an_r6_object
R6 對象有一個自動生成的clone() 方法,用於值複製,使用clone()複製的對象的變化不影響其餘對象。
a_value <- an_r6_object$clone()
當R6對象的一個或多個域包含另外一個R6對象時,默認clone() 經過地址傳送複製該R6域。
若是值複製這些R6域,clone() 方法必須使用參數:deep = TRUE。
a_deep_copy <- an_r6_object$clone(deep = TRUE)
(5)R6對象的消除
當消除對象時,定義公有finalize()方法運行自定義代碼。該方法通常用於關閉與數據庫或文件的鏈接,或者消除例如改變全局選項(option)或者圖形參數的反作用。
thing_factory <- R6Class( "Thing", public = list( initialize = function(x, y, z) { # do something }, finalize = function() { # undo something
# Print a message
"Disconnecting from the cooking times database."
# Disconnect from the database
dbDisconnect(private$conn) } ) )
當R的自動垃圾回收器(automated garbage collector)從存儲中清除對象時調用finalize() 方法,可以使用gc()強制垃圾回收。