Understanding Object-Oriented Programming in Ruby
Ruby is a fully object-oriented programming (OOP) language, meaning almost everything in Ruby is an object. Ruby’s OOP paradigm makes it easy to write clean, reusable, and modular code. In this blog, we’ll explore the fundamentals of object-oriented programming in Ruby, including classes, objects, inheritance, polymorphism, encapsulation, and modules — all explained with detailed examples.
1. What is Object-Oriented Programming?
OOP is a programming paradigm that organizes code into objects. Objects are instances of classes, which encapsulate data (attributes) and behaviors (methods). Ruby’s OOP model provides key features:
- Encapsulation: Hiding the internal state of objects and requiring all interactions to be performed through methods.
- Inheritance: Allowing a class to derive from another class, inheriting its methods and attributes.
- Polymorphism: Using a common interface for different data types or classes.
2. Classes and Objects
A class is a blueprint for creating objects. Objects are specific instances of a class. In Ruby, creating and using objects is intuitive.
Code Example:
# Define a class
class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end def greet
"Hello, my name is #{@name} and I am #{@age} years old."
end def birthday
@age += 1
"Happy Birthday! #{@name} is now #{@age} years old."
end
end# Create objects
person1 = Person.new("Alice", 30)
person2 = Person.new("Bob", 25)# Call methods on objects
puts person1.greet # Output: Hello, my name is Alice and I am 30 years old.
puts person2.birthday # Output: Happy Birthday! Bob is now 26 years old.
3. Inheritance
Inheritance allows a class (child) to inherit attributes and methods from another class (parent). Ruby uses the <
symbol to denote inheritance.
Code Example:
# Parent class
class Animal
attr_reader :name
def initialize(name)
@name = name
end def speak
"#{@name} makes a sound."
end
end# Child class
class Dog < Animal
def speak
"#{@name} barks."
end
end# Child class
class Cat < Animal
def speak
"#{@name} meows."
end
end# Create objects
dog = Dog.new("Rex")
cat = Cat.new("Whiskers")
puts dog.speak # Output: Rex barks.
puts cat.speak # Output: Whiskers meows.
4. Polymorphism
Polymorphism allows objects of different classes to respond to the same method call in their unique way. This is achieved through method overriding.
Code Example:
class Employee
def initialize(name, salary)
@name = name
@salary = salary
end
def details
raise "This method should be overridden by subclasses."
end
endclass Manager < Employee
def details
"#{@name} is a Manager earning $#{@salary} per year."
end
endclass Developer < Employee
def details
"#{@name} is a Developer earning $#{@salary} per year."
end
end# Use polymorphism
employees = [Manager.new("Alice", 100000), Developer.new("Bob", 80000)]
employees.each do |employee|
puts employee.details
end
# Output:
# Alice is a Manager earning $100000 per year.
# Bob is a Developer earning $80000 per year.
5. Encapsulation
Encapsulation restricts access to the internal state of an object and only allows modification through defined methods. Ruby achieves this by using private or protected methods.
Code Example:
class BankAccount
def initialize(balance)
@balance = balance
end
# Public method to display balance
def show_balance
"Your balance is $#{@balance}."
end # Public method to deposit money
def deposit(amount)
if valid_transaction?(amount)
@balance += amount
"Deposited $#{amount}. New balance is $#{@balance}."
else
"Invalid transaction."
end
end private # Private method to validate transactions
def valid_transaction?(amount)
amount > 0
end
endaccount = BankAccount.new(1000)
puts account.show_balance # Output: Your balance is $1000.
puts account.deposit(500) # Output: Deposited $500. New balance is $1500.
puts account.deposit(-100) # Output: Invalid transaction.
6. Modules and Mixins
Modules group reusable methods and can be mixed into classes using include
or extend
. They provide a way to share behaviors across multiple classes.
Code Example:
module Flyable
def fly
"#{self.class} is flying!"
end
end
class Bird
include Flyable
endclass Plane
include Flyable
endbird = Bird.new
plane = Plane.new
puts bird.fly # Output: Bird is flying!
puts plane.fly # Output: Plane is flying!
7. Advanced Example: Real-World Application
Let’s build a library system to demonstrate all OOP principles together.
Code Example:
module Searchable
def find_by_title(title)
@items.find { |item| item.title == title }
end
end
class Library
include Searchable def initialize
@items = []
end def add_item(item)
@items << item
end def list_items
@items.map(&:details).join("\n")
end
endclass Book
attr_reader :title def initialize(title, author)
@title = title
@creator = author
end def details
"Book: #{@title} by #{@creator}"
end
endclass DVD
attr_reader :title def initialize(title, director)
@title = title
@creator = director
end def details
"DVD: #{@title} directed by #{@creator}"
end
endlibrary = Library.new
book = Book.new("1984", "George Orwell")
dvd = DVD.new("Inception", "Christopher Nolan")library.add_item(book)
library.add_item(dvd)puts library.list_items
# Output:
# Book: 1984 by George Orwell
# DVD: Inception directed by Christopher Nolanitem = library.find_by_title("1984")
puts item.details # Output: Book: 1984 by George Orwell
Conclusion
Ruby’s object-oriented programming features provide the tools to create modular, reusable, and maintainable code. By mastering classes, objects, inheritance, polymorphism, encapsulation, and modules, you can leverage Ruby’s full potential to build scalable applications. Practice these concepts and experiment with your own projects to enhance your understanding!