Welcome back! We're now shifting our focus to another essential concept in Object-Oriented Programming (OOP): encapsulation. Encapsulation helps us bundle the data (variables) and the methods that operate on the data into a single unit called a class
. It also helps restrict access to some of the object's components.
In this lesson, you will learn how to:
- Define and use encapsulation: This involves hiding the internal state of an object and requiring all interaction to be performed through an object's methods.
- Create getter and setter methods: These methods allow controlled access to the object's properties using Ruby's
attr_accessor
,attr_reader
, andattr_writer
methods.
Let's look at the following Ruby example to gain a better understanding:
Ruby1# Person class with private data members name and age 2class Person 3 def initialize(name, age) 4 @name = name 5 @age = age 6 end 7 8 # attr_accessor automatically creates getter and setter for :name and :age 9 attr_accessor :name, :age 10end 11 12# Create a Person object and set the name and age 13person = Person.new("Alice", 30) 14person.name = "Bob" 15person.age = 25 16 17# Get the name and age of the person and print them 18puts "Name: #{person.name}, Age: #{person.age}"
In this example, we have a Person
class with private data members @name
and @age
. The attr_accessor
method automatically creates getter and setter methods, like name=
and age=
, for manipulating and accessing these private members.
In contrast if we use attr_reader
or attr_writer
, we can only create getter or setter methods, respectively. Here's an example:
Ruby1class Person 2 def initialize(name, age) 3 @name = name 4 @age = age 5 end 6 7 # attr_reader creates only getter methods for :name and :age 8 attr_reader :name, :age 9end 10 11person = Person.new("Alice", 30) 12puts "Name: #{person.name}, Age: #{person.age}" # Output: Name: Alice, Age: 30 13# person.name = "Bob" # Error: undefined method `name=' for #<Person:0x00007f8b1b
In this example, we can only read the name
and age
properties of the Person
object, but we can't modify them. If we try to set the name
property, we'll get an error because the setter method is not available.
Similarly, if we use attr_writer
, we can only create setter methods for the properties. Here's an example:
Ruby1class Person 2 def initialize(name, age) 3 @name = name 4 @age = age 5 end 6 7 # attr_writer creates only setter methods for :name and :age 8 attr_writer :name, :age 9end 10 11person = Person.new("Alice", 30) 12person.name = "Bob" 13person.age = 25 14# puts "Name: #{person.name}, Age: #{person.age}" # Error: undefined method `name' for #<Person:0x00007f8b1b
If we skip using attr_accessor
, attr_reader
, or attr_writer
, and directly access instance variables, we lose control over the data and can't enforce constraints or validations. Encapsulation allows us to protect the object's internal state and provide controlled access to it.
Encapsulation is fundamental in software development for several reasons:
- Data Protection: By using encapsulation, you protect object integrity by preventing accidental modification.
- Controlled Access: Through
attr_accessor
and similar methods, you can enforce constraints and validations. - Improved Maintainability: Encapsulation makes your code more modular and easier to maintain. Each
class
maintains its own state and behavior, so changes in one class usually don't affect others.
Understanding and applying encapsulation will make your code more secure, prevent bugs related to invalid states, and improve code clarity.
Ready to explore how encapsulation can make your code robust and secure? Let's head over to the practice section and implement these concepts together!