Slides
Classes and Instances
Ref. WGR Chapter 3, Organizing objects with classes
The Cookie Metaphor
- a class is a cookie cutter
- an instance is a cookie
- memory is cookie dough
- state is frosting
- users are hungry!
Example Class
class Cookie
def sweeten(more_chips = 10)
@chips ||= 0 # lazy initialization
@chips += more_chips
end
def yummy?
@chips and @chips >= 20
end
end
Creating and Using Objects
-
To create an object, call the new method on its class
cookie = Cookie.new
-
To call a method an object, use the dot operator
cookie.sweeten(50) cookie.yummy? #=> true
Constructors
- To instantiate an object, call the new method on its class
- The new method then calls initialize
class Cookie
def initialize
@chips = 0
end
end
cookie = Cookie.new # *not* Cookie.initialize!
Active vs. Lazy Initialization
Active initialization (inside the constructor) leads to simpler code elsewhere in the object, since other methods can assume the instance variables are ready to roll.
Active Initialization
class Cookie
def initialize
@chips = 0 # active initialization
end
def sweeten(more_chips = 10)
@chips += more_chips
end
def yummy?
@chips >= 20
end
end
Lazy Initialization
class Cookie
def sweeten(more_chips = 10)
@chips ||= 0 # lazy initialization
@chips += more_chips
end
def yummy?
@chips and # defensive coding
@chips >= 20
end
end
What does new
do?
- allocates memory for the instance
- calls initialize on the new instance
- returns a pointer to the instance
So by the time assignment (=
) happens, the object has been constructed and initialized.
So a constructor is your one big chance to initialize everything before anyone else gets a pointer to it.
Instance methods
- defined inside the class
- instance methods are shared among all instances
- same behavior, but different data
class Cookie
def bake
@temp = 350
end
end
Instance variables
- represent object state
- names start with an
@
- only visible inside the object
- i.e. when
self
is that object
- i.e. when
Getter and setter methods
class Person
def age=(years_old)
@age = years_old
end
def age
@age
end
end
alice = Person.new
alice.age= 17
alice.age #=> 17
alice.@age #=> SyntaxError
Ruby's setter sugar
alice.age = 17
is the same as
alice.age=(17)
- Technically, it's not an assignment, it's a method call
- ...but it looks like an assignment!
- that's called "syntactic sugar" since it makes the syntax sweeter
The setter gotcha
- Inside an object, you can't call that object's setter methods directly
class Person
def age=(years_old)
@age = years_old
end
def bar_mitzvah!
age = 13 # oops
end
end
- Why not?
- Because "
age = 13
" looks like a local variable assignment, which takes precedence - It eclipses the setter method!
- Syntax ambiguity! Oh noes!
- Because "
The setter gotcha solved
- Solution: use "
self.age
"- that forces it to be a method call
- or
@age
- that's a direct instance variable reference
class Person
def age=(years_old)
@age = years_old
end
def bar_mitzvah!
@age = 13
end
def bat_mitzvah!
self.age = 13
end
end
Attributes
- An attribute is a property with named getter and/or setter methods
- Usually corresponds to an instance variable
Attribute Shortcuts
aka "macros"
class Thing
attr_reader :age # def age; @age; end
attr_writer :age # def age=(x); @age = x; end
attr_accessor :age # both of the above
end
Constructor plus Attributes
class Person
attr_accessor :age
def initialize
@age = 20
end
end
alice = Person.new
alice.age #=> 20
Attribute Shortcuts (cont.)
- Can also take multiple arguments
class Thing
attr_accessor :foo, :bar
end
thing = Thing.new
=> #<Thing:0x007fe008897278>
>> thing.methods
=> [:foo, :foo=, :bar, :bar=,
Attribute Shortcuts (cont.)
- Wait a second!
- Q: Where are
attr_reader
et al. defined? - A: They are class methods of
Object
- A: Or maybe they're instance methods of
Class
orModule
; I'm not sure.
Attribute Shortcuts (cont.)
- Sadly,
attr_accessor
is misnamed - "accessor" means
reader
, butattr_accessor
makes a reader and a writer - Should have been called just
attribute
Lazy Initialization with Or-Equals
class Cookie
def chips
@chips ||= 10
end
end
Query methods
class Person
def child?
@age < 18
end
end
alice.age = 16
alice.child? #=> true
Note: query methods return a boolean by convention only
Bang methods
class Person
def birthday!
@age = @age + 1
end
end
- "
!
" is pronounced "bang" - usually means "watch out" or "destructive" or "side effect"
- could also mean "may raise an exception"
- no real rule, so watch out
- normally there's a non-bang equivalent
- in ActiveRecord, "
!
" means: raise exception if failure
A Poorly-Encapsulated Object
class BadStudent
attr_accessor :first_name, :last_name
end
joe = BadStudent.new
joe.first_name = "Joe"
joe.last_name = "Blow"
puts joe.first_name + " " + joe.last_name
A Well-Encapsulated Object
class GoodStudent
def initialize first_name, last_name
@first_name, @last_name = first_name, last_name
end
def full_name
"#{@first_name} #{@last_name}"
end
end
jane = GoodStudent.new("Jane", "Brain")
puts jane.full_name
A Well-Encapsulated Object (cont)
- Why is this well-encapsulated?
- initial state established by constructor
- internal state used by methods, not exposed by getters
- other objects do not have direct access to its internal state
- if requirements change, code only changes in one place
- e.g. adding a middle name
- Both shorter and safer than a poorly-encapsulated object
Labs
Outline
- Classes and Instances
- The Cookie Metaphor
- Example Class
- Creating and Using Objects
- Constructors
- Active vs. Lazy Initialization
- Active Initialization
- Lazy Initialization
- What does `new` do?
- Instance methods
- Instance variables
- Getter and setter methods
- Ruby's setter sugar
- The setter gotcha
- The setter gotcha solved
- Attributes
- Attribute Shortcuts
- Constructor plus Attributes
- Attribute Shortcuts (cont.)
- Attribute Shortcuts (cont.)
- Attribute Shortcuts (cont.)
- Lazy Initialization with Or-Equals
- Query methods
- Bang methods
- A Poorly-Encapsulated Object
- A Well-Encapsulated Object
- A Well-Encapsulated Object (cont)
- Labs