Quick Introduction to Object Oriented Programming

David Morales
The Startup
Published in
5 min readJan 24, 2021
Lego pieces

The object oriented programming concept is rather old, it first appeared in the 1970s from the Smalltalk language, and it describes the use of objects and messages in a computer program. Basically the objects are the actors of the movie (the program), and the messages form the script.

The most popular languages used for web development support object oriented programming (OOP), being Ruby a pure object oriented language from the beginning. So I will use it to show you the OOP concepts in this article.

Class

We can understand a class as something that describes a concept. For instance, let’s suppose we have a computer class that describes how a computer is. I will use Von Neumann’s architecture:

Von Neumann’s architecture
Von Neumann’s architecture

Essentially (and to make things simple) a computer should have a CPU, memory, and input/output devices. Let’s write a class:

class Computer
attr_reader :cpu, :memory, :input, :output
def initialize
@cpu = "Generic CPU"
@memory = "Generic memory"
@input = "Generic input"
@output = "Generic output"
end
def to_s
"Computer specs: \n" +
"CPU: #{cpu} \n" +
"Memory: #{memory} \n" +
"Input device: #{input} \n" +
"Output device: #{output} \n"
end
end

The parts of our generic computer are called properties. The initialize method is the constructor, because it’s called automatically when the class is instantiated (more on this in the next section). The to_s method is special as well, you will see why in the next section.

Object

Because a class is only describing something, we need to instantiate it into an object to actually use that description.

We already have a computer description, we know how a computer is. So if we buy one, we will have our computer. Let’s buy it:

my_computer = Computer.new

Yay! Now we can inspect our computer:

puts my_computer

When defining the to_s method, it will be called automatically by Ruby when displaying the object itself. So in this case it will be called and it will display this information:

Computer specs:
CPU: Generic CPU
Memory: Generic memory
Input device: Generic input
Output device: Generic output

Yeah, we have a generic computer. But no one has a generic computer. Every computer has computer parts built by a brand. And that brings us to the next concept.

Inheritance

This concept can be understood as a relationship between two classes. One is the parent, and the other one is the child.

In Ruby, a parent can have multiple children, but a child can only have one parent. There are other languages where a child can have multiple parents (multi-inheritance).

The child will inherit all properties and methods from the parent.

Let’s build a Macbook Pro:

class MacbookPro < Computer
def initialize
@cpu = "Intel Core i5"
@memory = "8 GB DDR3"
@input = "Keyboard"
@output = "13-inch with Retina display"
end
end

Here we have inherited everything from the Computer class, but we have defined the initialize method again. This action is known as override, we have overridden the constructor.

Now when we instantiate an object and display it, the to_s method will be called, and because we have not overridden it, the Computer code will be taken.

my_macbook_pro = MacbookPro.new
puts my_macbook_pro
Computer specs:
CPU: Intel Core i5
Memory: 8 GB DDR3
Input device: Keyboard
Output device: 13-inch with Retina display

From this point we could customize our Macbook Pro adding more input and output devices, but let’s keep it simple.

Ruby has a mechanism that can be used to replicate multi-inheritance: modules. So let’s create a module for portable computers:

module PortableComputer
attr_reader :battery
@battery = "Generic battery"
end

Now I will include the brand new PortableComputer module, initialize the battery property and override the to_s method of our Macbook Pro class:

class MacbookPro < Computer
include PortableComputer
def initialize
@cpu = "Intel Core i5"
@memory = "8 GB DDR3"
@input = "Keyboard"
@output = "13-inch with Retina display"
@battery = "Built-in 74.9-watt-hour lithium-polymer"
end
def to_s
super +
"Battery: #{battery} \n"
end
end

Hey wait! What’s that super thing? It is used to get the parent’s code. So in this case I’m getting all the to_s code, and then appending the battery string.

Running our code again should display this:

Computer specs:
CPU: Intel Core i5
Memory: 8 GB DDR3
Input device: Keyboard
Output device: 13-inch with Retina display
Battery: Built-in 74.9-watt-hour lithium-polymer

Encapsulation

Did you notice how we displayed the specs? We just called puts followed by the object. Ruby called to_s automatically and the specs were displayed.

The way we built the output string inside the method gives the class the encapsulation concept, because when we use puts, we don’t know how to display that information, we just call the object and that’s it. It’s the responsibility of the class to do it, so using the object is simple and straightforward.

So encapsulation can be understood as hiding the internal representation of the object (our to_s method implementation) from the outside (when using the object).

Let’s suppose that Apple let us to upgrade our Macbook Pro’s memory. This can be achieved by implementing a new method in the class:

class MacbookPro < Computer
# all the existing code
def upgrade_memory
@memory = "16 GB DDR3"
end
end

Now from the outside we can upgrade memory just calling that method. We don’t need to know how to do it, so we achieved encapsulation. Let’s see it in action:

my_macbook_pro = MacbookPro.new
my_macbook_pro.upgrade_memory
puts my_macbook_pro

It should display 16 GB of memory now.

Polymorphism

Polymorphism is when we provide a common action for different classes. While the action is apparently the same, it has a different implementation for each class.

Let’s come back to our example and tweak our Macbook Pro’s upgrade_memory method:

class MacbookPro < Computer
# all the existing code
def upgrade_memory
puts "Sorry, memory is soldered and cannot be upgraded."
end
end

Now let’s create a new computer, a 27-inch iMac:

class IMac27 < Computer
def initialize
@cpu = "Intel Core i5"
@memory = "8 GB DDR3"
@input = "Keyboard"
@output = "27-inch with Retina display"
end
def upgrade_memory
@memory = "16 GB"
end
end

Instantiate and call the objects like this:

my_imac_27 = IMac27.new
my_imac_27.upgrade_memory
puts my_imac_27
putsmy_macbook_pro = MacbookPro.new
my_macbook_pro.upgrade_memory
puts my_macbook_pro

This is the output:

Computer specs:
CPU: Intel Core i5
Memory: 16 GB
Input device: Keyboard
Output device: 27-inch with Retina display
Sorry, memory is soldered and cannot be upgraded.
Computer specs:
CPU: Intel Core i5
Memory: 8 GB DDR3
Input device: Keyboard
Output device: 13-inch with Retina display
Battery: Built-in 74.9-watt-hour lithium-polymer

The computer specs are from our iMac. We can see that memory was successfully upgraded. But then we could not upgrade our Macbook Pro’s memory. It’s displaying our error message, and then the specs still show 8 GB.

Conclusion

I encourage you to modify the sample code I have used for this article. Play with it and try to break things. It’s a good way to consolidate your new knowledge and understand it even better.

Although I have used Ruby, the concepts are basic and are the same for the rest of languages.

--

--

David Morales
The Startup

Exploring the intersections of productivity, economy, business, marketing, and software engineering for growth and success. 💻💼📈🚀