In this post I will be going over Object-Oriented Programming (OOP) and object-orientation in Ruby. If Ruby is your first OOP you may have come across this concept and perhaps found it a little tricky, well you're not alone! We'll go over what object-oriented programming is and its application in the Ruby language.
What is Object-Oriented Programming?
Wikipedia defines OOP as follows
Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).
In other words, the underlying concept of OOP is that everything is an object. Objects, as we know, contain data in the form of attributes (also known as properties) and functions that run some code (also known as methods). The most common form of OOP is class-based. In class-based OOP, objects are defined via classes, which act as an abstract, reusable template (or blueprint) for instances (objects) of that class. For example, an object(aka instance) named bob
could belong to a class named Person
, or an object named dog
could belong to a class named Animal
, and so on. A class can also define the names of attributes and/or methods an instance of said class should have, the instance will then inherit the attributes and methods of the parent class when it's created.
Let's look at an example:
Source: https://www.educative.io/blog/object-oriented-programming
In the above example, there is a class (Car) that contains attributes representing the color, brand, and model of a Car object. We can then instantiate 2 objects from the class Car (myCar and helensCar) that will inherit the attributes and methods of its parent class (Car).
Here's an example of what this looks like in Ruby:
#Classes in Ruby and other OOP languages follow the camel case rule; names must be uppercase and no spaces are allowed.
class Car
def initialize(color, brand, model)
@color = color
@brand = brand
@model = model
end
#Instance methods are declared in the parent class and are available across all instances of the parent class.
def repaint(new_color)
@color = new_color
end
end
#Instantiates a new object (charger) with the .new method
charger = Car.new('red', 'Dodge', 'Charger')
p charger
# => #<Car:0x00000000012973d0 @color="red", @brand="Dodge", @model="Charger">
charger.repaint('blue')
p charger
# => #<Car:0x00000000012973d0 @color="blue", @brand="Dodge", @model="Charger">
An object is instantiated by calling the .new
method on a class followed by the arguments that will become the instance variables (attributes) of that instance. Any methods declared in the parent class will automatically be inherited. In order to create an object with the instance variables of the parent class, a special method is needed: initialize
.
The Initialize method and instance variables/methods.
When instantiating an object, more often than not you will be passing one or more arguments to create instance variables for that object. In order to accomplish this, you will first need to declare a special Ruby method in the parent class, called initialize
.
Lets look at that method from the previous example:
def initialize(color, brand, model)
@color = color
@brand = brand
@model = model
end
If you've come across object-orientation in other languages, such as JavaScript, this is also known as a constructor method. It is a special method that is used for creating and initializing an instance object of a class.
You'll notice that within the method are variables being declared with a leading @ character. The @ character symbolizes an instance variable. Instance variables are variables that are available across all instances of a class. In the previous example, every object that's instantiated from the Car
class with have the variables @color
, @brand
, and @model
with their own given values. The repaint
method is an instance method, and thus will also be available to all instances of that class.
Getter/Setter methods
In order to directly access object variables from outside of the class, i.e. to be able to call charger.color
and have it return red
, we need to implement getter methods. A getter method is simply a method that allows you to read the value of an instance variable.
class Car
#...
#Getter methods defined below
def color
@color
end
def brand
@brand
end
def model
@model
end
end
charger = Car.new('red', 'Dodge', 'Charger')
p charger.color
# => "red"
p charger.brand
# => "Dodge"
p charger.model
# => "Charger"
We defined 3 getter methods named after an instance variable and simply had them return the value of that variable. Pretty straightforward!
If we didn't have a getter method for an instance variable, we would get an error when trying to read the variable:
charger.color
# => undefined method `color' for #<Car:0x00000000012973d0> (NoMethodError)
Now what if we wanted to change the value of a variable? Since we can now access variables directly using getter methods, we should just as easily be able to change the values of said variables... right?
charger.color = 'blue'
# => undefined method `color=' for #<Car:0x0000000002616668> (NoMethodError)
#Did you mean? color
Notice the part that reads undefined method 'color='
, then the part that reads Did you mean? color
. color
is an already established getter method within our class, however color=
is not currently an existing method, hence the error.
The trailing =
character in a method name (such as color=
) designates a method as a setter method, therefore color
and color=
are two totally separate methods.
Setter methods are set up like so:
class Car
#...
#Setter methods defined below
def color=(color)
@color = color
end
def brand=(brand)
@brand = brand
end
def model=(model)
@model = model
end
end
Now, we're able to both read variable values as well as change them.
p charger.color
# => "red"
charger.color = 'blue'
p charger.color
# => "blue"
A little factoid: Since =
symbolizes a setter method. We can also call our method like charger.color=(blue)
and get the same result. Thankfully, thanks to syntax sugar we are able to write it as charger.color = 'blue'
for better readability.
Accessor methods
Now if you're thinking that having to write getters and setters for every single variable is a lot of work, you thought right. A cornerstone of coding is the DRY principle, and writing repetitive code is not only time consuming but ugly to look at.
Thankfully, Ruby gives us an easy way to set up our getters and setters in one line of code! Enter accessor methods.
There are 3 accessor methods available to make our lives easier:
-
attr_reader
: For creating getter methods -
attr_writer
: For creating setter methods -
attr_accessor
: For creating both getter and setter methods at once!!
attr_accessor
allows us to create both getters and setters in a single line of code. Let's refactor our code to use this method.
class Car
#Accessor methods
attr_accessor :color, :brand, :model
def initialize(color, brand, model)
@color = color
@brand = brand
@model = model
end
def repaint(new_color)
@color = new_color
end
end
We call attr_accessor
and pass in our variable arguments as Symbols. This creates both attr_reader
and attr_writer
for each variable all in one line.. much better!
Everything is an object
Going back to the concept of everything is an object, does that mean that strings, arrays, numbers, etc are objects too? Yes, they are!
When you create an array or a string, you are creating an object from a built-in Ruby class!!
We can demonstrate this by calling the .class
method, which shows us the class the object belongs to.
p [].class
# => Array
p "Hello World".class
# => String
p 123.class
# => Integer
Arrays, strings, and numbers belong to the Array
, String
, and Integer
classes respectively. These are some of the built-in classes within Ruby with their own methods and variables. Think about when you're using the .capitalize
or .reverse
method on a string, those are instance methods belonging to the String
class!
Conclusion
These are some of the basics of object-oriented programming in Ruby. With this knowledge you're ready for other OOP concepts such as self, class variables, and class methods. Good luck and have fun!
Top comments (0)