Ruby Classes and Objects
Ruby is a pure Object-Oriented Programming language where everything is an object. This chapter will provide a detailed introduction to classes and objects in Ruby, and how to use them to build programs.
🎯 Object-Oriented Basics
What are Classes and Objects?
- Class: Template or blueprint for objects, defining attributes and behaviors
- Object: Instance of a class, with specific attribute values and executable behaviors
ruby
# Everything is an object
puts 42.class # Integer
puts "Hello".class # String
puts true.class # TrueClass
puts nil.class # NilClass
# View methods of an object
number = 42
puts number.methods # Display all available methods🏗️ Class Definition
Basic Class Definition
ruby
# Define a simple class
class Person
# Class body
end
# Create objects (instantiation)
person1 = Person.new
person2 = Person.new
puts person1.class # Person
puts person2.class # Person
puts person1.object_id # Object ID
puts person2.object_id # Different object IDConstructor initialize
ruby
class Person
# Constructor (initialization method)
def initialize(name, age)
@name = name # Instance variable
@age = age
end
# Instance method
def introduce
puts "I am #{@name}, #{@age} years old"
end
end
# Create object and call method
person = Person.new("Alice", 25)
person.introduce # Output: I am Alice, 25 years oldAccessor Methods
ruby
class Person
def initialize(name, age)
@name = name
@age = age
end
# getter methods
def name
@name
end
def age
@age
end
# setter methods
def name=(name)
@name = name
end
def age=(age)
@age = age
end
def introduce
puts "I am #{@name}, #{@age} years old"
end
end
# Using accessor methods
person = Person.new("Bob", 30)
puts person.name # Bob
person.age = 31
puts person.age # 31Using attr_accessor to Simplify Accessors
ruby
class Person
# Automatically generate getter and setter methods
attr_accessor :name, :age
# Equivalent to:
# attr_reader :name, :age # Generate only getter
# attr_writer :name, :age # Generate only setter
def initialize(name, age)
@name = name
@age = age
end
def introduce
puts "I am #{@name}, #{@age} years old"
end
end
# Using simplified accessors
person = Person.new("Charlie", 28)
puts person.name # Charlie
person.name = "David"
person.age = 29
person.introduce # I am David, 29 years old📦 Variable Types
Instance Variables
ruby
class Counter
def initialize
@count = 0 # Instance variable, starts with @
end
def increment
@count += 1
end
def count
@count
end
end
counter1 = Counter.new
counter2 = Counter.new
counter1.increment
counter1.increment
puts counter1.count # 2
puts counter2.count # 0 (different instance)Class Variables
ruby
class Counter
@@total_instances = 0 # Class variable, starts with @@
def initialize
@count = 0
@@total_instances += 1
end
def increment
@count += 1
end
def count
@count
end
# Class method to access class variable
def self.total_instances
@@total_instances
end
end
counter1 = Counter.new
counter2 = Counter.new
counter3 = Counter.new
puts Counter.total_instances # 3Global Variables
ruby
# Global variable, starts with $ (not recommended for heavy use)
$global_counter = 0
class GlobalCounter
def increment
$global_counter += 1
end
def self.global_count
$global_counter
end
end
counter = GlobalCounter.new
counter.increment
counter.increment
puts GlobalCounter.global_count # 2Local Variables and Constants
ruby
class MathConstants
PI = 3.14159 # Constant, uppercase naming
E = 2.71828
def self.calculate_circle_area(radius)
local_variable = radius * radius # Local variable
PI * local_variable
end
end
puts MathConstants::PI # 3.14159
puts MathConstants.calculate_circle_area(5) # 78.53975🎯 Method Types
Instance Methods
ruby
class Calculator
def add(a, b)
a + b
end
def subtract(a, b)
a - b
end
end
calc = Calculator.new
puts calc.add(10, 5) # 15
puts calc.subtract(10, 5) # 5Class Methods
ruby
class MathUtils
# Method 1: Using self
def self.square(number)
number * number
end
# Method 2: Using class name
def MathUtils.cube(number)
number * number * number
end
# Method 3: Define inside class
class << self
def power(base, exponent)
base ** exponent
end
end
end
puts MathUtils.square(4) # 16
puts MathUtils.cube(3) # 27
puts MathUtils.power(2, 3) # 8Private Methods
ruby
class BankAccount
def initialize(balance)
@balance = balance
end
def deposit(amount)
if valid_amount?(amount)
@balance += amount
puts "Deposit successful, current balance: #{@balance}"
else
puts "Invalid amount"
end
end
def withdraw(amount)
if valid_amount?(amount) && sufficient_funds?(amount)
@balance -= amount
puts "Withdrawal successful, current balance: #{@balance}"
else
puts "Withdrawal failed"
end
end
def balance
@balance
end
private # Private methods
def valid_amount?(amount)
amount > 0
end
def sufficient_funds?(amount)
@balance >= amount
end
end
account = BankAccount.new(1000)
account.deposit(500) # Deposit successful, current balance: 1500
account.withdraw(200) # Withdrawal successful, current balance: 1300
# account.valid_amount?(100) # Error: private methodProtected Methods
ruby
class Animal
def initialize(name)
@name = name
end
protected # Protected methods
def speak
"#{@name} makes a sound"
end
end
class Dog < Animal
def initialize(name)
super(name)
end
def bark
speak + " Woof!" # Can call parent's protected method
end
end
dog = Dog.new("Buddy")
puts dog.bark # Buddy makes a sound Woof!
# dog.speak # Error: protected method🧬 Inheritance
Basic Inheritance
ruby
# Parent class
class Vehicle
def initialize(brand, model)
@brand = brand
@model = model
end
def start_engine
puts "#{@brand} #{@model} engine started"
end
def stop_engine
puts "#{@brand} #{@model} engine stopped"
end
def info
"#{@brand} #{@model}"
end
end
# Subclass
class Car < Vehicle
def initialize(brand, model, doors)
super(brand, model) # Call parent class constructor
@doors = doors
end
def open_trunk
puts "Open trunk"
end
# Override parent method
def info
super + " (#{@doors} doors)"
end
end
# Using inheritance
car = Car.new("Toyota", "Corolla", 4)
car.start_engine # Toyota Corolla engine started
car.open_trunk # Open trunk
puts car.info # Toyota Corolla (4 doors)Method Lookup Chain
ruby
class A
def method1
"A's method1"
end
end
class B < A
def method1
"B's method1"
end
def method2
"B's method2"
end
end
class C < B
def method1
"C's method1"
end
end
obj = C.new
puts obj.method1 # C's method1
puts obj.method2 # B's method2 (inherited from B)🔄 Modules and Mixins
Module Definition
ruby
# Define module
module Flyable
def fly
"I'm flying!"
end
def land
"I landed!"
end
end
module Swimmable
def swim
"I'm swimming!"
end
end
# Using modules
class Bird
include Flyable # Include module
end
class Fish
include Swimmable
end
class Duck
include Flyable
include Swimmable
end
bird = Bird.new
puts bird.fly # I'm flying!
duck = Duck.new
puts duck.fly # I'm flying!
puts duck.swim # I'm swimming!Modules as Namespaces
ruby
module MathUtils
PI = 3.14159
class Calculator
def self.add(a, b)
a + b
end
end
def self.square(number)
number * number
end
end
puts MathUtils::PI # 3.14159
puts MathUtils::Calculator.add(2, 3) # 5
puts MathUtils.square(4) # 16🎭 Polymorphism
Method Overriding and Polymorphism
ruby
class Animal
def speak
"Animal makes a sound"
end
end
class Dog < Animal
def speak
"Woof!"
end
end
class Cat < Animal
def speak
"Meow!"
end
end
class Bird < Animal
def speak
"Chirp chirp!"
end
end
# Polymorphism example
animals = [Dog.new, Cat.new, Bird.new]
animals.each do |animal|
puts animal.speak # Calls appropriate method based on object type
end
# Output:
# Woof!
# Meow!
# Chirp chirp!🧱 Object Lifecycle
Initialization and Cleanup
ruby
class Resource
def initialize(name)
@name = name
puts "Creating resource: #{@name}"
end
def use
puts "Using resource: #{@name}"
end
# Called before garbage collection
def finalize
puts "Cleaning up resource: #{@name}"
end
# Custom destructor method
def dispose
puts "Manually cleaning up resource: #{@name}"
end
end
# Object lifecycle
resource = Resource.new("Database connection")
resource.use
resource.dispose # Manual cleanup
# resource variable will be garbage collected after going out of scope🎯 Advanced Features
Singleton Class
ruby
class MyClass
def instance_method
"Instance method"
end
class << self
def class_method
"Class method"
end
end
end
# Singleton methods
obj = MyClass.new
class << obj
def singleton_method
"Singleton method"
end
end
puts obj.instance_method # Instance method
puts MyClass.class_method # Class method
puts obj.singleton_method # Singleton method
# Check singleton class
puts obj.singleton_class
puts MyClass.singleton_classMethod Missing Handling
ruby
class FlexibleObject
def initialize
@data = {}
end
def method_missing(method_name, *args, &block)
if method_name.to_s.end_with?('=')
# Setter method
key = method_name.to_s.chomp('=').to_sym
@data[key] = args.first
else
# Getter method
@data[method_name]
end
end
def respond_to_missing?(method_name, include_private = false)
true
end
end
obj = FlexibleObject.new
obj.name = "Alice"
obj.age = 25
puts obj.name # Alice
puts obj.age # 25🧪 Practice Examples
Complete Bank Account System
ruby
class BankAccount
attr_reader :account_number, :balance
attr_accessor :owner
@@total_accounts = 0
def initialize(owner, initial_balance = 0)
@owner = owner
@balance = initial_balance
@account_number = generate_account_number
@@total_accounts += 1
end
def deposit(amount)
if amount > 0
@balance += amount
log_transaction("Deposit", amount)
true
else
puts "Deposit amount must be greater than 0"
false
end
end
def withdraw(amount)
if amount > 0 && amount <= @balance
@balance -= amount
log_transaction("Withdrawal", amount)
true
elsif amount > @balance
puts "Insufficient balance"
false
else
puts "Withdrawal amount must be greater than 0"
false
end
end
def transfer_to(other_account, amount)
if withdraw(amount)
other_account.deposit(amount)
puts "Transfer successful: #{amount} to #{other_account.owner}"
end
end
def self.total_accounts
@@total_accounts
end
def info
"Account: #{@account_number}, Owner: #{@owner}, Balance: #{@balance}"
end
private
def generate_account_number
"ACC#{rand(100000..999999)}"
end
def log_transaction(type, amount)
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
puts "[#{timestamp}] #{type}: #{amount}, Balance: #{@balance}"
end
end
# Using bank account system
account1 = BankAccount.new("Alice", 1000)
account2 = BankAccount.new("Bob", 500)
puts account1.info
puts account2.info
account1.deposit(200)
account1.withdraw(150)
account1.transfer_to(account2, 300)
puts "Total accounts: #{BankAccount.total_accounts}"🎯 Object-Oriented Best Practices
1. Single Responsibility Principle
ruby
# Good design: Each class has one responsibility
class User
attr_accessor :name, :email
def initialize(name, email)
@name = name
@email = email
end
end
class UserValidator
def self.valid_email?(email)
email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
end
end
class UserPersistence
def self.save(user)
# Save user to database
end
end2. Encapsulation
ruby
class Temperature
def initialize(celsius)
self.celsius = celsius
end
def celsius
@celsius
end
def celsius=(value)
raise ArgumentError, "Temperature cannot be below absolute zero (-273.15°C)" if value < -273.15
@celsius = value
end
def fahrenheit
@celsius * 9.0 / 5.0 + 32
end
def fahrenheit=(value)
self.celsius = (value - 32) * 5.0 / 9.0
end
end
temp = Temperature.new(25)
puts temp.celsius # 25
puts temp.fahrenheit # 77.0
temp.fahrenheit = 86
puts temp.celsius # 30.03. Inheritance Hierarchy Design
ruby
# Abstract base class
class Shape
def area
raise NotImplementedError, "Subclass must implement area method"
end
def perimeter
raise NotImplementedError, "Subclass must implement perimeter method"
end
end
class Rectangle < Shape
def initialize(width, height)
@width = width
@height = height
end
def area
@width * @height
end
def perimeter
2 * (@width + @height)
end
end
class Circle < Shape
def initialize(radius)
@radius = radius
end
def area
Math::PI * @radius ** 2
end
def perimeter
2 * Math::PI * @radius
end
end
# Using polymorphism
shapes = [
Rectangle.new(5, 3),
Circle.new(4)
]
shapes.each do |shape|
puts "Area: #{shape.area.round(2)}, Perimeter: #{shape.perimeter.round(2)}"
end📚 Next Steps
After mastering Ruby classes and objects, continue learning:
- Ruby Object-Oriented Programming - Deep dive into OOP concepts
- Ruby Inheritance and Polymorphism - Learn advanced inheritance features
- Ruby Modules - Master modular programming
- Ruby Design Patterns - Learn common design patterns
Continue your Ruby learning journey!