What is Object Oriented Programming?

Object-oriented programming is a programming paradigm based on the concept of “objects”, which may contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods.

A class defines the behavior of objects by describing their attributes and their relationship to other classes. The class is also used when selecting methods, functions that behave differently depending on the class of their input. Classes are usually organised in a hierarchy: if a method does not exist for a child, then the parent’s method is used instead; the child inherits behavior from the parent.

R’s three OO systems

Reference classes (RC)

RC objects behave more like objects do in most other programming languages, e.g., Python, Ruby, Java, and C#. RC classes are best used for describing stateful objects, objects that change over time.

Defining classes and creating objects

# setRefClass() generates new objects
Account <- setRefClass("Account")
Account$new()
## Reference class object of class "Account"
# how to create fields ?
Account <- setRefClass("Account",
  fields = list(balance = "numeric"))

# access field using $
a <- Account$new(balance = 100)
a$balance
## [1] 100
# mutate field 
a$balance <- 200
a$balance
## [1] 200
# RC objects are mutable
b <- a
b$balance
## [1] 200
a$balance <- 0
b$balance
## [1] 0
# For this reason, RC objects come with a copy() method that allow you to make a copy of the object
c <- a$copy()
c$balance
## [1] 0
a$balance <- 100
c$balance
## [1] 0
# Add behaviour to objects through methods
# The deep assignment arrow, <<-, never creates a variable in the current environment,
# but instead  modifies an existing variable found by walking up the parent environments.
Account <- setRefClass("Account",
  fields = list(balance = "numeric"),
  methods = list(
    withdraw = function(x) {
      balance <<- balance - x
    },
    deposit = function(x) {
      balance <<- balance + x
    }
  )
)

a <- Account$new(balance = 100)
a$deposit(100)
a$balance
## [1] 200

Inheritence

# contains is the name of the parent RC class to inherit behaviour from.
NoOverdraft <- setRefClass("NoOverdraft",
  contains = "Account",
  methods = list(
    withdraw = function(x) {
      if (balance < x) stop("Not enough money")
      balance <<- balance - x
    }
  )
)
accountJohn <- NoOverdraft$new(balance = 100)
accountJohn$deposit(50)
accountJohn$balance
## [1] 150
accountJohn$withdraw(200)

All reference classes eventually inherit from envRefClass.

Recognizing objects and methods

is(accountJohn, "refClass")
## [1] TRUE
pryr::otype(accountJohn)
## [1] "RC"

Method dispatch

Method dispatch is very simple in RC because methods are associated with classes, not functions. When you call x$f(), R will look for a method f in the class of x, then in its parent, then its parent’s parent, and so on. From within a method, you can call the parent method directly with callSuper(...).

# contains is the name of the parent RC class to inherit behaviour from.
NoOverdraft <- setRefClass("NoOverdraft",
  contains = "Account",
  methods = list(
    withdraw = function(x) {
      if (balance < x) stop("Not enough money")
      #balance <<- balance - x
      callSuper(x)
    }
  )
)
accountJohn <- NoOverdraft$new(balance = 100)
accountJohn$deposit(50)
accountJohn$balance
## [1] 150