While the functionality for credit
might seem trivial, in reality crediting an account will require a lot of checks (e.g. sanctioned countries and terrorist financing). So, let us create now a little more useful example with a function debet()
, because before debiting an account, one will need to check if there is enough balance.
# debet # Generic function to debet an account # Arguments: # x -- account object # y -- numeric -- the amount to be taken from the account # Returns # confirmation of action or lack thereofdebet <- function(x,y){ useMethod()} # Make it a generic function that verifies the balance # before the account a debet is booked. setGeneric(“debet”) ## [1] “debet” # Add the debet() function as a method for objects of type CurrAcc setMethod(“debet”, c(“CurrAcc”), function(x, y) { if(x @balance >=y) { new_bal <-x @balance +y} else{ stop(“Not enough balance.”) } new_bal } ) ## [1] “debet” # Test the construction:my_curr_acc @balance # for reference## [1] 600 my_curr_acc @balance <-debet(my_curr_acc, 50) my_curr_acc @balance # the balance is changed## [1] 650 # We do not have enough balance to debet 5000, so the # following should fail:my_curr_acc @balance <-debet(my_curr_acc, 5000) ## Error in debet(my_curr_acc, 5000): Not enough balance.my_curr_acc @balance # the balance is indeed unchanged:## [1] 650
Warning – Overloading functions
If you want to overload an existing function such as union()
and exp()
, then you should of course not define the function first – as in the first line in the aforementioned code. Doing so will make its original definition unavailable.
In its most simple form, S4 dispatching is similar to S3 dispatching. However, the implementation for S4 is more powerful. When an S4 generic function dispatches on a single class with a single parent, then S4 method dispatch is no different from the S3 method dispatch.
For practical use, the main difference is how default values are defined. In S4, there is a special class “ANY” that functions as the default.
Similarly to S3, S4 knows about group generics. Further, details can be found in the function documentation (visualize the documentation – as usual – with the command ?S4groupGeneric
. There is also a function to call the “parent method”: callNextMethod()
.
S4groupGeneric()
callNextMethod()
The main advantage of S4 is that it is possible to dispatch on multiple arguments (though it all becomes a little more complicated and abstract). That is the case when you have multiple parent classes. The rules can be found in the documentation of the function Methods
.
Methods()
These aspects require utmost care. Personally, the author believes that this is where the S4 OO implementation stops to be practically relevant. When one needs further dependencies and complexity the S4 method becomes too easy too complex and it might be hard to predict which method will be called.
For this reason, it might be better to avoid multiple inheritance and multiple dispatch unless absolutely necessary.
Finally, there are methods that allow us to identify which method gets called, given the specification of a generic call:
selectMethod(“credit”, list(“CurrAcc”)) ## Method Definition: ## ## function (x, y) ## { ## new_bal <- x@balance + y ## new_bal ## } ## ## Signatures: ## x ## target “CurrAcc” ## defined “CurrAcc”
There is a lot more to say about S4 inheritance and method dispatch, though it is not really necessary in the rest of the book. Therefore, we refer to other literature. For example, “Advanced R” by Wickham (2014) is a great source.
6.4. The Reference Class, refclass, RC or R5 Model
RC
R5
The reference class OO system is also known as “RC,” “refclass” or as “R5.” It is the most recent 7 OO implementation and it introduces a message-passing OO system for R.
refclass
Note – Recent developments
Reference classes are reasonably new to R and therefore, will develop further after the publication of this book. So for the most up-to-date information, we refer to R itself: type ?ReferenceClasses
in the command prompt to see the latests updates in your version of R.
People with OOP background will naturally feel more comfortable with RC, it is something what people with C++, C#, PHP, Java, Python, etc., background will be familiar with: a messagepassing OO implementation. However, that sense of comfort has to be mended, in many ways, the refclass system in R is a combination of S4 and environments.
OOP
object oriented
programming
That said, the RC implementation brings R programming to a next level. This system is particularly suited for larger projects, and it will seamlessly collaborate with S4, S3 and base types. However, note that the vast majority of packages in R does not use RC, actually none of the most often used packages do. This is not only because the pre-date the refclass system but also because they do not need it (even while some are rather complex).
Using RC in R will add some complexity to your code and many people advice to use the refclass system only where the mutable state is required. This means that even while using R5, it is still possible to keep most of the code functional.
6.4.1 Creating RC Objects
R provides the function setRefClass
to create a new R5 class definition that can immediately be used to create an instance of the object.
setRefClass()
The RC object has three main components, given by three arguments to its creator function setRefClass
:
1 contains: The classes from which it inherits – note that only other refclass objects are allowed;
2 fields: These are the attributes of the class – they are the equivalent of “slots” in S4; one can supply them via a vector of field names or a named list of field types;
3 methods: These functions are the equivalent for for dispatched methods in S4, and they operate within the context of the object and canmodify its fields. While it is possible to add these later, this is not good programming practice, makes the code less readable and will lead to misunderstandings.
We will illustrate this with a remake of the example about bank accounts.
# Note that we capture the returned value of the setRefClass # Give this always the same name as the class.account <- setRefClass(“account”, fields = list(ref_number = “numeric”, holder = “character”, branch = “character”, opening_date = “Date”, account_type = “character” ), # no method yet.) x_acc <-account $new(ref_number = 321654987, holder = “Philippe”, branch = “LDN05”, opening_date = as.Date( Sys.Date()), account_type = “current” ) x_acc ## Reference class object of class “account” ## Field “ref_number”: ## [1] 321654987 ## Field “holder”: ## [1] “Philippe” ## Field “branch”: ## [1] “LDN05” ## Field “opening_date”: ## [1] “2020-01-30” ## Field “account_type”: ## [1] “current”
Читать дальше