We have updated the content of our program. To access the current Software Engineering curriculum visit curriculum.turing.edu.
Object-Oriented Programming
Learning Goals
- Understand the distinction between a variable and what it references
- Understand how objects can interact
- Understand the process Ruby use to look up an instance method
- Understand self
Vocabulary
- Variable
- Object
- Reference
- Pointer
- Self
Object Oriented Programming
Variables and Objects
There is an important distinction between variables and what that variable holds. Take a look at this example:
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog_variable = Dog.new("Fido")
other_dog_variable = dog_variable
Turn and Talk: How many Dog
objects are there in this example?
There is only one Dog
object in this example. Ruby creates an Object when it does .new
. The line other_dog_variable = dog_variable
DOES NOT create another Dog
object, it creates another variable.
Variables are not Objects themselves. They merely reference the Object. You will also hear this as, variables point to objects. We can diagram this like so:
dog_variable---> #<Dog:0xA007F...> <---other_dog_variable
In this case, two variables reference the same object. We can demonstrate this like so:
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog_variable = Dog.new("Fido")
other_dog_variable = dog_variable
dog_variable.name = "Lassie"
puts other_dog_variable.name
Turn and Talk: What will be printed to the screen?
Let’s look at another example
class Owner
attr_reader :pet
def initialize(pet)
@pet = pet
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Fido")
owner = Owner.new(dog)
Turn and Talk: How many Dog
objects are there in this example? For each Dog
object, which variables reference that object?
Bonus: What is the scope of those variables?
Method Look Up
We can think of methods as messages. Just like messages they have receivers. When you type the code some_object.method_name
, you are sending the method_name
message to the some_object
object. Another way to say this is that methods run on objects. When the Object receives that message, it looks in its Class to find that method.
Let’s look at this example:
class Owner
attr_reader :name,
:pet
def initialize(name, pet)
@name = name
@pet = pet
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Fido")
owner = Owner.new("Jeff", dog)
puts owner.name
Both these Objects have a method called name
, so if we call .name
, how does Ruby know which one to call? The key here is that methods run on Objects. In this example, we are calling .name
on an Owner
object (remember, the variable owner
is storing an Owner object), so Ruby will execute the name
method in the Owner
Class.
self
Let’s add to this example:
class Owner
attr_reader :name,
:pet
def initialize(name, pet)
@name = name
@pet = pet
end
def introduction
introduce_self + " and " + introduce_pet
end
def introduce_self
"Hello, my name is #{@name}"
end
def introduce_pet
"My pet's name is #{@pet.name}"
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Fido")
owner = Owner.new("Jeff", dog)
puts owner.introduction
Turn and Talk: With your partner, walk through this code and explain to each other what it is doing.
The introduction
method calls two other methods: introduce_self
and introduce_pet
. We just said that methods run on objects, so what objects are these methods running on? The answer is self
. Whenever we call methods without the dot notation, there is an implied receiver of self
. Change the example to use self:
class Owner
attr_reader :name,
:pet
def initialize(name, pet)
@name = name
@pet = pet
end
def introduction
self.introduce_self + " and " + self.introduce_pet
end
def introduce_self
"Hello, my name is #{@name}"
end
def introduce_pet
"My pet's name is #{@pet.name}"
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Fido")
owner = Owner.new("Jeff", dog)
puts owner.introduction
If we run this code, it will still work exactly the same. self
means “the current object”, so when Ruby sees self
inside the introduction
method, it is referring to the Owner
object that we called introduction
on, i.e. the Owner named “Jeff”. Ruby then knows to look in the Owner
class for the introduce_self
and introduce_pet
methods.
You’ve run across the error undefined local variable or method
. Now we can see how Ruby can treat something as a local variable or a method. In this case, it first looks for a local variable named introduce_self
and introduce_pet
. When it can’t find them, it then looks for a method. We can illustrate this by changing the example to:
class Owner
attr_reader :name,
:pet
def initialize(name, pet)
@name = name
@pet = pet
end
def introduction
introduce_self = "Hello"
introduce_pet = "goodbye."
introduce_self + " and " + introduce_pet
end
def introduce_self
"Hello, my name is #{@name}"
end
def introduce_pet
"My pet's name is #{@pet.name}"
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
dog = Dog.new("Fido")
owner = Owner.new("Jeff", dog)
puts owner.introduction
Practice: With your partner, look at the following code example.
- For each
binding.pry
, try to predict what the value ofself
will be on that line of code. Check your answers by running the code and typingself
into the pry session: - For each
puts
, try to predict what will be output to the screen.
require 'pry'
class Owner
attr_reader :name,
:pet
def initialize(name, pet)
@name = name
@pet = pet
binding.pry
end
def introduction
self.introduce_self + " and " + self.introduce_pet
binding.pry
end
def introduce_self
"Hello, my name is #{@name}"
end
def introduce_pet
"My pet's name is #{@pet.name}"
end
end
class Dog
attr_accessor :name
def initialize(name)
@name = name
binding.pry
end
end
binding.pry
dog = Dog.new("Fido")
owner_1 = Owner.new("Dan", dog)
owner_2 = Owner.new("Terry", dog)
puts owner_2.introduction
dog.name = "Lassie"
puts owner_1.introduction
puts "owner_1 pet name is #{owner_2.pet.name}"