Object-Oriented Programming (OOP) with Ruby

Jun 19, 2018 - 8 min read
Object-Oriented Programming (OOP) with Ruby
Share

You feel frustrated, and concerned about the quality of your code. You’re convinced that your performance as a Ruby developer is below expectations.

You struggle to keep up with all the feature requests and bug fixes.

And you know you can do much better, if only you could learn that damn Object-Oriented Programming junk that everyone keeps raving about.

But it doesn’t have to be like that. While learning about OOP is definitely required knowledge to be considered a good Ruby developer, you don’t have to start there. Actually, you can’t start there.

You first need to understand why OOP is good, and what is it good for.

So let’s take it slow and figure this thing out.

What is Object-Oriented Programming (OOP)?

OOP tries to solve the problem of spaghetti code (unstructured and difficult to maintain) by offering a simplified method of programming.

It is a programming technique that promotes code reuse by structuring code into modular components (called objects). We will talk more about objects in a second.

Why do you need OOP?

OOP has become required knowledge these days. You can see it as a line item on any job posting out there, and you can hear developers talking about it everywhere.

If you’re just starting out as a junior developer, you most likely won’t understand any of it. And that’s OK. After all, your code works just fine even without knowing anything about OOP.

But OOP is not just about writing code that works.

OOP is about happiness

As professional developers, we spend most of our days writing code. And, if you’re like me, you’re spending a good chunk of your free time thinking about code.

So it’s safe to conclude that coding is a big part of your life and thus a big influence on your happiness.

How you spend your day matters. If all you do all day is staring at broken code, and putting out fires, then you won’t enjoy your job very much. Your life will be miserable, and you will wish you’ve chosen a different career.

Writing code should be fun. And it is. The secret lies in how you write code.

Object-Oriented Programming makes developers happy.

OOP is about productivity

Closely tied to the level of happiness you get out of writing code, is productivity. If the bulk of your time is spent on fixing broken code, that time is not spent on writing new features or improving existing ones.

On the other hand though, if all you do is write new features, or improve on existing ones, then your productivity goes through the roof, and you will become a much more valuable asset to your company.

Object Oriented Programming will boost your productivity by making code easier to change and maintain.

How do I learn OOP?

My advice to you is, you first get some real world programming experience by writing a ton of code. You don’t have to worry about the quality of the code, you’ll have no way of judging that if you’ve never written much code before.

After a while, you will start looking for ways to improve your craft, like how to write less code, and how to organize it better, etc.

These small optimisations will add up over time, and they will form a base for your journey into the Object-Oriented Programming world.

What I’m trying to say is, OOP will come to you gradually as a need to improve your code, and you will most likely discover parts of it by accident.

And then, if you do pick up a book on OOP, or read about it somewhere else, you will have a much easier time understanding what it means, and how it can help you.

What is an object?

An object is a way to structure your code. It contains two things: data (or properties) and logic (or methods). You can think about objects as self-contained pieces of functionality that can communicate with other objects via messages.

OOP

For example, let’s say we are trying to build a dashboard for a car and we have a GasMeter object, and a GasTank object. The GasMeter would contain logic about displaying information, while the GasTank would contain logic about measuring the amount of gas a car has.

So if the GastTank wants to get any data back from the GasTank, it would have to send it a message (e.g. GasTank.gas_level).

What is a class?

A class is an easy way to create multiple objects of the same type. It’s like a template.

For example, if you have a Car class you can create a subaru object, or a mustang object. As you can imagine, these two objects, while they are both cars, they have slightly different properties (e.g. the subaru might be blue while the mustang might be red).

How does OOP work?

OOP offers four principles that you can use to improve your code.

Encapsulation

Encapsulation is a mechanism for hiding data from the outside of an object. It is a concept where data and methods that work on that data are bundled together in one unit.

class Person
  def initialize(name)
    @name = name
  end
end

Person.new("Joe").name
# => NoMethodError (undefined method `name' for #<Person:...>)

In this example, the data (the instance variable) is private.

class Person
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

Person.new("Joe").name # => "Joe"

By declaring a method that returns the value of the instance variable, you can now access the data inside the object. That is because while data is private, methods are public by default.

Inheritance

Inheritance permits code reusability by specifying common traits for different objects by defining a parent/child (or is-a) relationship.

Inheritance

The class that is used as the basis for inheritance is called a superclass, while the class that inherits from a superclass is called a subclass or derived class, but parent class and child class are also acceptable terms.

class Person
  # We're defining a `name` and `age` methods so we can access the
  # `@name` and `@age` instance variables by sending messages to each
  # of the objects (`jim` and `joe`).
  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  def description
    name + " is " + age.to_s + " years old"
  end
end

class Child < Person
  def initialize(name, age, hobbies)
    super(name, age)
    @hobbies = hobbies
  end
end

class Father < Person
  def initialize(name, age, job_title)
    super(name, age)
    @job_title = job_title
  end
end

joe = Child.new("Joe", 18, ["music", "coding"])
joe.description # => Joe is 18 years old

jim = Father.new("Jim", 45, "CEO")
jim.description # => Jim is 45 years old

By using inheritance, we’ve eliminated the redundant code. Instead of defining the description method on both Child and Father, you can define it in the Person class. The Person class is now a superclass for Child and Father, and you can access it calling super in any subclass.

If you want to see the full list of superclasses, you can use the ancestors method on any object.

Polymorphism

Methods can behave differently based on the type of object you are calling them on. In other words, you are able to send the same message to different objects and get back different data.

One way to achieve polymorphism is by using inheritance.

class Person
  def initialize(name)
    @name = name
  end

  def my_name
    @name
  end
end

class Child < Person
end

class Father < Person
  def my_name
    "My name is #{@name}."
  end
end

joe = Child.new("Joe")
jim = Father.new("Jim")

[joe, jim].each(&:my_name) # => Joe, My name is Jim

By calling the same message on different type of objects you can have a different behavior based on the object’s type.

But, you can also achieve runtime polymorphism through duck typing.

Duck typing

Duck types rely on common interfaces rather than inheritance.

Duck Typing

The saying goes that if it walks like a duck and talks like a duck, it’s a duck. In other words, any type of object can be used as a receiver as long as it responds to the required message.

class Friend
  def name
    "Chris"
  end
end

class Child
  def name
    "John"
  end
end

class Father
  def greeting(person)
    "Hi #{person.name}"
  end
end

In the example above, we have two completely different types (Friend and Child) that respond to the same message (i.e. they have a common interface), the name message.

So as long as the argument to greeting responds to the name message, everyone’s happy.

Do I need to learn OOP before I can start writing Ruby code?

No, you don’t! It’s actually very hard to understand OOP if you don’t have any experience writing code.

Only by writing a lot of code you start to feel the need for improvement. And once you do, you will naturally discover OOP as a way to improve your coding skills.

12 Project Ideas
Cezar Halmagean
Software development consultant with over a decade of experience in helping growing companies scale large Ruby on Rails applications. Has written about the process of building Ruby on Rails applications in RubyWeekly, SemaphoreCI, and Foundr.