Lesson 1
Understanding Ruby Classes: A Refresher on Attributes and Methods
Overview

Welcome! Today, we’re diving into Ruby classes, the foundation of Object-Oriented Programming (OOP) in Ruby. This lesson introduces the concept of classes as blueprints for objects. We’ll explore how to create classes and objects, understand the difference between them, and see how attributes and methods store and manipulate data within each object.

Ruby Classes Refresher

In Ruby, a class serves as a blueprint for creating objects. Each object, or instance of a class, has its own data (attributes) and can perform actions (methods). Imagine a video game character: each character has attributes like health or strength, and methods like attack or defend.

Here’s a simple GameCharacter class to illustrate these ideas:

Ruby
1class GameCharacter 2 # Constructor method to initialize attributes 3 def initialize(name, health, strength) 4 @name = name # instance variable for name 5 @health = health # instance variable for health 6 @strength = strength # instance variable for strength 7 end 8 9 # Method to simulate an attack 10 def attack(target) 11 target.health -= @strength # Reduces target's health by strength 12 end 13 14 # Accessors to allow reading and modifying instance variables externally 15 attr_accessor :name, :health, :strength 16end

Here, GameCharacter defines a blueprint for characters. The attributes @name, @health, and @strength store data specific to each character, while the attack method defines behavior each character can perform.

Creating Classes and Objects

In Ruby, creating an object is as simple as defining a class and calling new on that class. Each object created this way is an instance of the class and has its own copy of the class’s attributes.

Ruby
1character_1 = GameCharacter.new("Hero", 100, 20) 2character_2 = GameCharacter.new("Villain", 80, 15) 3 4puts character_1.name # Output: Hero 5puts character_2.health # Output: 80

Each instance, character_1 and character_2, has its own unique data, demonstrating how a class can produce multiple distinct objects.

Understanding Attributes in Ruby Classes

Attributes, represented by instance variables prefixed with @, hold data specific to each instance. In the GameCharacter class, @name, @health, and @strength are instance variables storing each character's state. In Ruby, instance variables are private by default, meaning they cannot be accessed directly from outside the class. Instead, we need getter and setter methods to retrieve and update these values.

Using attr_reader and attr_writer

Ruby provides a convenient way to create getter and setter methods through attr_reader and attr_writer, allowing controlled access to instance variables.

attr_reader creates a getter method only, allowing an instance variable to be read but not modified from outside the class.

Ruby
1class Person 2 def initialize(name) 3 @name = name # private instance variable 4 end 5 6 attr_reader :name # allows reading @name outside the class 7end 8 9person = Person.new("Alice") 10puts person.name # Output: Alice 11# person.name = "Bob" # Error: undefined method `name='

In this example, attr_reader provides read-only access to the @name variable, protecting it from unintended modifications.

Similarly, attr_writer creates a setter method only, allowing the instance variable to be modified but not read directly. This is useful for write-only data.

Ruby
1class Secret 2 def initialize(secret_code) 3 @secret_code = secret_code # private instance variable 4 end 5 6 attr_writer :secret_code # allows modifying @secret_code outside the class 7end 8 9secret = Secret.new("1234") 10secret.secret_code = "5678" # Update only, no reading method 11# puts secret.secret_code # Error: undefined method `secret_code'

Here, attr_writer restricts access to writing only, making it ideal for sensitive information like a password or secret code.

Using attr_accessor

Ruby’s attr_accessor combines both getter and setter methods, allowing an instance variable to be both read and modified from outside the class.

Ruby
1class Account 2 def initialize(balance) 3 @balance = balance # private instance variable 4 end 5 6 attr_accessor :balance # allows both reading and modifying @balance 7end 8 9account = Account.new(100) 10puts account.balance # Output: 100 11account.balance = 200 12puts account.balance # Output: 200

In this example, attr_accessor simplifies access by enabling both read and write functionality, making it a versatile choice for variables that need full access.

Defining Methods to Add Behavior

Methods define actions or behaviors for each object. The attack method in GameCharacter defines a simple interaction between characters. By calling this method, one character can reduce another character's health.

Ruby
1puts character_2.health # Output: 80 (before being attacked) 2puts character_1.strength # Output: 20 (the strength of the attacker) 3character_1.attack(character_2) # Decrease the health of character 2 by the strength of attacker 4puts character_2.health # Output: 60 (after being attacked)

In this example, attack modifies the health attribute of the target character, illustrating how methods operate on an object's state.

Example: A Simple BankAccount Class

To deepen our understanding, let’s look at another example, BankAccount, which models a real-world entity with attributes and methods. This class includes attributes like the account holder's name and balance, and methods for depositing and withdrawing money.

Ruby
1class BankAccount 2 # Initializes a new account with holder's name and an optional starting balance (default 0). 3 def initialize(holder_name, balance = 0) 4 @holder_name = holder_name # Stores the account holder's name 5 @balance = balance # Stores the account balance 6 end 7 8 # Adds a positive amount to the balance. 9 def deposit(amount) 10 if amount > 0 11 @balance += amount 12 puts "#{amount} deposited. New balance: #{@balance}" 13 else 14 puts "Deposit amount must be positive." 15 end 16 end 17 18 # Deducts a valid amount from the balance if sufficient funds are available. 19 def withdraw(amount) 20 if amount > 0 && amount <= @balance 21 @balance -= amount 22 puts "#{amount} withdrawn. Remaining balance: #{@balance}" 23 else 24 puts "Insufficient balance or invalid amount." 25 end 26 end 27 28 # Getter and setter for holder_name and balance 29 attr_accessor :holder_name, :balance 30end 31 32# Creates a new account and performs some transactions. 33account = BankAccount.new("Alex", 1000) 34account.deposit(500) # Outputs "500 deposited. New balance: 1500" 35account.withdraw(200) # Outputs "200 withdrawn. Remaining balance: 1300" 36puts "#{account.holder_name}'s balance: #{account.balance}" # Outputs "Alex's balance: 1300"

This BankAccount class shows how attributes and methods can encapsulate both data and functionality, making it easy to create multiple independent bank accounts with their own balances and behaviors.

Lesson Summary and Practice

In this lesson, we explored the fundamentals of classes and objects in Ruby. We learned:

  • How to define a class with attributes and methods.
  • How to create instances (objects) from a class.
  • How attributes store each object's unique data, while methods define its behaviors.

Now, it's time to move to the practice section to reinforce these concepts. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.