Object Oriented Programming in Ruby by Example

There is lots of information out there about object oriented programming (OOP), but if your like me I find most of this information to be much too abstract. So, rather than make one more academic blog post about the four principles of OOP I will take you through a hands on example written in Ruby.

Data Abstraction

The first principle of OOP we are going to cover is data abstraction. Data abstraction is just a fancy way of saying that we are going to model our data to look like it's real life equivalent. So, how do we model a ship? Well a ship should be an object that has a position and an acceleration constant. It should also be able to accelerate and move. Translating this into Ruby and we get something like this.

class Ship ACCELERATION = 1 def initialize @position = { x: 0, y: 0 } @speed = { x: 0, y: 0 } end def acceleration(direction) # ADDITIONAL CODE end def move # ADDITIONAL CODE end end

A basic Ruby class that represents a space ship.

This is what is called a class. A class is just a blueprint that is use to create objects that represent some structured data which in this case is a ship. Without getting into too much detail ACCELERATION is a constant that is global in scope and can be accessed using Ship::ACCELERATION outside the class or just ACCELERATION within the class. @position and @speed are instance variables which have local scope and therefore can only be accessed from within the class and don't actually exist until an object is created. initialize is a constructor method which creates an object when called with Ship.new. Finally, acceleration and move are instance methods that are global in scope and can therefore be accessed from outside a class as well as within a class. From outside the class they are accessed using objectInstanceName.methodName and from within the class using methodName for example ship.move and move.


Encapsulation just means that all of the implementation details of the object being modeled are hidden and only the methods and properties required to use the object are exposed. For our Ship class @position and @speed are hidden as they do not need to be known outside the class since they are just used by the methods acceleration and move. The methods acceleration and move on the other hand are public since the main body of the script will need to call these methods to cause the ship to accelerate or move. Finally, the ACCELERATION constant is public, but it cannot be changed. This is encapsulation at work. The ship has a clearly defined interface for it's use namely the acceleration and move methods and the rest of it's implementation is either hidden from view or cannot be altered. This is important because it allows for us to alter the class definition for example the move or acceleration methods, but it will not affect any other part of the program which uses Ship since the interface remains the same. Call accelerate to accelerate and move to move.


Inheritance is a real time saver as it will allow us to create classes, or new object blueprints, which will inherit constants, properties, and methods from it's parent class. This allows for us to create a class hierarchy or taxonomy if you will. In our example we will add a new class which will represent a special kind of space ship; a fighter. Fighters are just like any other spaceship except they can also fire missiles! So, instead of having to add all of the same kinds of constants, properties, and methods we will just make Fighter a subclass of Ship. Once this is done the only thing for us to do is add a method to fire missiles.

class Fighter < Ship def fire_missle # ADDITIONAL CODE end end

An example of class inheritance where Fighter inherits all of the constants, properties, and methods of Ship and then adds a new method fire_missles.

Pretty cool right? You should note, however, that a class can only inherit from one other class. Classes can be chained together though creating an inheritance tree. For example Stealth_Fighter < Fighter < Ship. Modules are used in Ruby to simulate the multiple inheritance of other OOP languages, but we are not going to cover these here.


Where do they come up with these names? Latin of course! Polymorphism means many forms which aptly describes the ability of OOP languages to use the same identifier to cause different behavior. So far in our program Fighter has inherited all of it's implementation details from it's parent class Ship except for the fire_missle method, but this doesn't seem quite right. Fighters should have a greater acceleration and the ability to keep track of how many missiles they currently have. So lets make these changes using polymorphism.

class Fighter < Ship ACCELERATION = 2 def initialize super() @missiles = 10 end end

An example of polymorphism through the redeclaration of ACCELERATION and the modification of initialize.

Using polymorphism we can alter or completely replace inherited features, but we don't have to change their identifiers, or names, or the interface that is used to use or access them! In this example we have replaced the original acceleration constant with one that is twice as much. We have also extended the original initialize method to include a new instance variable @missles which will keep track of how many missiles our fighter has at any given time. The super() method calls the wrapping method, initialize in this case, of the parent class. This is needed for inheritance to be maintained because when a method is redefined in a child class it completely replaces the inherited method. A single call to super() fixes this issue. With polymorphism it doesn't matter if we are using a ship or a fighter or that they are implemented differently we just tell them to move or accelerate and their classes take care of all of the details and that is the power of polymorphism and OOP.


That's it. Well not really. OOP is a large field with many nuances for you to explore, but hopefully this little tutorial has helped you better understand OOP and Ruby. Take it easy and happy coding.