Object Oriented Programming

Students cheer on the Redhawks during a sporting event at Miami University.

Before this point, only functional programming has been used. Functional programming deals with stringing functions together to accomplish a task. Object oriented programming is concerned with objects and what methods or functions an object has.

4 Basic Principles of Object Oriented Programming

There are 4 basic principles that are used in object oriented programming. These are:

  • Encapsulation: hiding unnecessary details from the user (example: the inner workings of an ATM)
  • Inheritance: reusing code from one class that is very similar to the code that is needed for another class
  • Abstraction: using what we know to work with something without completely knowing how it works (example: most of us don't know how to build a remote control from scratch, but we still know how to use it)
  • Polymorphism: taking a base class method and overwriting it so that the child class has the same method name, but a different implementation specific for that child class. 

S3

In R, there are different packages that can be used for object oriented programming. The S3 package is the most fundamental and is the first one we will discuss. S3 methods have a different notation than regular methods. The outline for an S3 method is given below:

generic.class

To see all S3 methods available, the following code may be run:

.S3PrimitiveGenerics

## [1]  "anyNA"          "as.character" "as.complex" "as.double"
## [5] "as.environment" "as.integer" "as.logical" "as.numeric"
## [9] "as.raw" "c" "dim" "dim<-"
## [13] "dimnames" "dimnames<-" "is.array" "is.finite"
## [17] "is.infinite" "is.matrix" "is.na" "is.nan"
## [21] "is.numeric" "length" "length<-" "levels<-"
## [25] "names" "names<-" "rep" "seq.int"
## [29] "xtfrm"

S3 Method Creation

An S3 method can be created by the user. The basic template for this is:

nameOfMethod <- function(x, otherArguments, …) { UseMethod(“nameOfMethod”) }

Much of this code is very similar to the code on the Using and Creating Functions, with a few differences.

Similarities:

  • First give the method name
  • Second, put <-
  • Next, write the code for the function to do something. In the case above, the UseMethod is used, which gets the code of another method
  • Lastly surround code with curly braces

Differences:

  • The UseMethod calls the code of an already made method
  • Specify the name of the method inside the parentheses

The code below is used to assign classes to an object:

# Assigning Classes to an Object
class(car) <- c("vehicle", "sedan", "AllWheel")

# Ask if car inherits from vehicle, sedan, and and AllWheel vector
inherits(car, "vehicle")
## [1] TRUE
inherits(car, "sedan")
## [1] TRUE
inherits(car, "AllWheel")
## [1] TRUE

# Ask if car is a character vector
is.character(car)
## [1] TRUE

# Ask if car inherits from train
inherits(car, "train")
## [1] FALSE

R6 Package

The R6 package is another package for object oriented programming. It has more of the syntax and types of statements used in object oriented programming in languages like C++ or java. Below is some code for starting with R6:

carMaker <- R6Class(
"Car",
private = list(
  attribute = "value",
  attribute2 = 10
 )
)

The attributes can be any kind of variable (Boolean, numeric, etc,).

Creating an object of the new class

The following code will create a car class:

# New car
myCar <- carMaker$new()

Now, we have a car object.

Private vs. public

Private was used above, but it will be explained here along with the public keyword. Private and public are helpful to use for encapsulation. Private hides the methods that the user shouldn't see, while public shows the methods a user should see. An example with the cars objects is below:

Initialize Method

To specify how to initialize an object, the initialize method is shown below:

carMaker <- R6Class(
  "Car",
  private = list(
    fasterBy10 = 10,
    slow = 0
  ),
  public = list(
    speed = 0,
    getFaster = function(faster) {
      speed = faster
      print("You're getting faster!")
    },
    # Initialize Method
    initialize = function(faster, slow, ...) {
      if(!missing(faster)) {
         self.faster <- faster
      }
      if(!missing(slow)) {
         private$slow <- slow
      }
    }
  )
)
# new car newCar <- carMaker$new(faster = 50, slow = -5)

Creating Some Access

Even though variables are created private, sometimes they need to be accessed safely. This can be done using the active keyword and binding. The code with the car class is below:

#Using active keyword
carMaker <- R6Class(
  "Car",
  private = list(
    fasterBy10 = 10,
    slow = 0
faster = 0 ), public = list( speed = 0, getFaster = function(fast) { speed = fast print("You're getting faster!") }, # Initialize Method initialize = function(faster, slow, ...) { if(!missing(fasterCar)) { private$faster <- fasterCar } if(!missing(slower)) { private$slow <- slower } } ), active = list( # Binding fasterBy10Fun = function() { return(private$fasterBy10) } ) )
# new car newCar <- carMaker$new(faster = 50, slow = -5) # fasterBy10 value newCar$fasterBy10Fun

The code above allows us to get the value of the private variable. It is called a getter method.

Need a Refresher?

Go back to the beginner tutorials.