The Big R-Book. Philippe J. S. De Brouwer
= “custBank”), contains = c(“account”) # methods go here )
Let us now come back to the current account and define it while adding some methods (though as we will see later, it is possible to add them later too).
# Definition of RC object currentAccount currAccount <- setRefClass(“currentAccount”, fields = list(interest_rate = “numeric”, balance = “numeric”), contains = c(“account”), methods = list( credit = function(amnt) { balance <<- balance + amnt }, debet = function(amnt) { if (amnt <= balance) { balance <<- balance - amnt } else { stop(“Not rich enough!”) } } ) ) # note how the class reports on itself: currAccount ## Generator for class “currentAccount”: ## ## Class fields: ## ## Name: ref_number holder branch ## Class: numeric character character ## ## Name: opening_date account_type interest_rate ## Class: Date character numeric ## ## Name: balance ## Class: numeric ## ## Class Methods: ## “debet”, “credit”, “import”, “.objectParent”, ## “usingMethods”, “show”, “getClass”, “untrace”, ## “export”, “.objectPackage”, “callSuper”, “copy”, ## “initFields”, “getRefClass”, “trace”, “field” ## ## Reference Superclasses: ## “account”, “envRefClass”
Notice that, the assignment operator within an object is the local assignment operator: <<-
. The operator <<-
will call the accessor functions if defined (via the object$accessors()
function).
We can now create accounts and use the methods supplied.
ph_acc <- currAccount$new(ref_number = 321654987, holder = “Philippe”, branch = “LDN05”, opening_date = as.Date(Sys.Date()), account_type = “current”, interest_rate = 0.01, balance = )
Now, we can start using the money and withdrawing money.
ph_acc$balance # after creating balance is 0: ## [1] 0 ph_acc$debet(200) # impossible (not enough balance) ## Error in ph_acc$debet(200): Not rich enough! ph_acc$credit(200) # add money to the account ph_acc$balance # the money arrived in our account ## [1] 200 ph_acc$debet(100) # this is possible ph_acc$balance # the money is indeed gone ## [1] 100
The Reference Class OO implementation R5 uses the $
to access attributes and methods.
It is also possible – though not recommendable – to create methods after creation of the class.
alsoCurrAccount <- setRefClass(“currentAccount”, fields = list( interest_rate = “numeric”, balance = “numeric”), contains = c(“account”) ) alsoCurrAccount$methods(list( credit = function(amnt) { balance <<- balance + amnt }, debet = function(amnt) { if (amnt <= balance) { balance <<- balance - amnt } else { stop(“Not rich enough!”) } } ))
In general, R is very flexible in how any of the OO systems is implemented. However, the refclasses do not allow to add fields after creating. This makes sense because it would make all existing objects invalid, since they would not have those new fields.
6.4.2 Important Methods and Attributes
All refclass object inherit from the same superclass envRefClass
, and so they get at creation some hidden fields and methods. For example, there is .self
. This variable refers to the object itself.
Other common methods for R5 objects that are always available (because they are inherited from envRefClass
) are the following, illustrated for an object of RC class named RC_obj
:
RC_obj$callSuper(): This method initializes the values defined in the super-class.
RC_obj$initFields(): This method initializes the values fields if the super-class has no initialization methods.
RC_obj$copy(): This method creates a copy of the current object. This is necessary because Reference Classes classes don't behave like most R objects, which are copied on assignment or modification.
RC_obj$field(): This method provides access to named fields, it is the equivalent to slots for S4. RC_obj$field(“xxx”) the same as RC_obj$xxx. RC_obj$field(“xxx”, 5), the same as assigning the value via RC_obj$xxx <- 5.
RC_obj$import(x): This method coerces x into this object, and RC_obj$export(Class) coerces a copy of RC_obj into that class.
6.5. Conclusions about the OO Implementation
The OO system that R provides is unlike what other OO languages provide. In the first place, it offers not only a method-dispatching system but also has a message-passing system. Secondly, it is of great importance that it is possible to use R without even knowing that the OO system exists. In fact, for most of the following chapters in this book, it is enough to know that the generic-function implementation of the OO logic exists.
Digression – R6
Note that we did not discuss the R6OOsystem. R6 is largely the same as R5 but adds some important OO features to the mix, such as private and public functions and properties. However, it is not widely used and most probably will become obsolete in further versions of R5.a
R6
Three or even four OO systems in one language is a lot, but that complexity does not stand in the way of practical applications. It seems that a little common sense allows the user to take the best of what is available, even when mixing systems.
So what system to use?
1 For a casual calculation of a few hundred lines of code, it is probably not necessary to define your own classes. You probably will use S3 implicitly in the packages that you will load, but you will not have to worry about it at all: just use plot() and print() and expect it to work.
2 If your inheritance structure is not too complex (and so you do not need multi-argument method signatures) and if your objects will not change themselves then S3 is the way to go. Actually, most packages on CRAN use S3.
3 If still your objects are static (not self-modifying), but you need multi-argument method signatures, then S4 is the way to go. You might want to opt for S4 also to use the formalism, as it can make code easier to understand for other people. However, be careful with the inheritance structure, it might get quite complex.
4 If your objects are self-modifying, then RC is the best choice. In S3 and S4, you would