More About Classes
Scope of instance variables within a class
class Person
def set_name( name )
@name = name
end
def set_weight( weight )
puts @name
@weight = weight
end
end
jane = Person.new
jane.set_name("jane")
jane.set_weight(50)
Class instance variables are accesible from anywhere within your class.
Getters and Setters
Having created an instance variable in our object, we might want to read that outside our object. However, we have to define a method that will act as an interface for reading for this variable called a Getter Method.
class Person
def initialize(name)
@name = name
end
def name
@name
end
end
Now we can read or get the name outside the object.
person = Person.new("Jane")
person.name
# => "Jane"
Similarly, we may need to change or set an instance variable from outside the object, so we create a method called a setter method.
class Person
def initialize(name)
@name = name
end
def name
@name
end
def name=(other)
@name = other
end
end
We can now get and set the name of a person using the methods we created for @name
.
person = Person.new("Samantha")
person.name
# => "Samantha"
person.name = "Sam"
person.name
# => "Sam"
Getters and Setters, the Ruby way
In ruby, the practice of creating a getter method is so common there is a shorthand that can be used at the top of a class definition called attr_reader
.
class Person
attr_reader :name
def initailize(name)
@name = name
end
def name=(other)
@name = other
end
end
Similarly, we can do the same with the setter method using attr_writer
class Person
attr_reader :name
attr_writer :name
def initialize(name)
@name = name
end
end
Moreover, we have a shorthand for telling our class to create both a getter and a setter method called attr_accessor.
class Person
attr_accessor :name
def initialize(name)
@name = name
end
end
Private Methods
If we create a class Person
with a name attribute and use attr_accessor
to create the getters and setters as follows
class Person
attr_accessor :name
attr_accessor :weight
attr_accessor :location
def initialize(name)
@name = name
@weight = '1kg'
@location = 'SG'
end
end
then anyone can read and access Person#name
.
person1 = Person.new("John")
person1.name
# => "John"
As part of creating a set of clean interfaces for our users (of this class) sometimes we want to make things inaccessible.
We can use the private
keyword. Everything under the private keyword is private outside the class.
Example: We want to output a string for weight. Let's keep track of the number weight in a separate instance variable and convert it into the proper string depending on their location.
We don't need to expose these methods to the user of the class.
class Person
def initialize(name)
@name = name
@location = 'SG'
end
def weight
if location == 'USA'
return standard_weight
end
metric_weight
end
# some other code goes here to set the location
private
def standard_weight
weight = @weight * 2.2
"#{weight} lbs"
end
def metric_weight
"#{@weight} kgs"
end
end
We can also add private methods by defining new methods below the private
keyword
class Person
def initialize(name)
@name = name
end
private
attr_accessor :name
def make_call
puts "Calling friends"
end
end
Note that we can create a public method that calls a private method, because we are within the class.
class Person
def initialize(name)
@name = name
end
def call
make_call if name
end
private
attr_accessor :name
def make_call
puts "Calling friends"
end
end
Reference vs. Value
What is an object in ruby? Basically everything that isn't a keyword.
However, this can cause you some headaches if you're not careful.
Imagine we had the following
arr1 = Array.new
arr1.push 1
arr1.push 2
arr2 = arr1
arr1 << 4
#=> [1,2]
arr2
#=> [1,2]
Wow, the second array completely changed. That's because arr2
was a reference to arr1
. Both variables represented the same object. The way around this is to copy the object.
arr1 = Array.new
arr1.push 1
arr1.push 2
arr2 = Array.new(arr1)
arr1 << 3
#=> [1,2,3]
arr2
#=> [1,2]
This doesn't happen with single value variables:
my_num = 1
my_other_num = my_num
puts my_other_num
my_other_num += 2
puts my_other_num
puts my_num
Ruby and javscript need to be careful about the size of things- how much memory things take up. If they are not sure of the size of an array or object, the value they need to store the variable is a pointer/reference to a thing, rather than the thing itself ( a pointer to arr1
vs. the value 1
itself)
Chainable methods
What if I wanted to create a class that had chainable methods calling many methods in one line.
class Person
def initialize(name)
@name = name
end
def greet
puts "Hello I am #{@name}."
puts "What is your name?"
@other = gets.chomp
puts "Nice to meet you, #{@other}."
end
def thank
puts "Thank you for coming."
end
def farewell
puts "Farewell, #{@other}"
end
end
Trying to do:
person1 = Person.new("john")
person1.greet.thank.farewell
#=> NoMethodError: undefined method `thank' for nil:NilClass
to achieve this we have to return a reference to the object after each method
class Person
def initialize(name)
@name = name
end
def greet
puts "Hello I am #{@name}."
puts "What is your name?"
@other = gets.chomp
puts "Nice to meet you, #{@other}."
self
end
def thank
puts "Thank you for coming."
self
end
def farewell
puts "Farewell, #{@other}"
self
end
end