Classes in Ruby—and other languages—are a way of organizing objects into mini code-factories: where the class itself is a template that gets filled out for each copy, or instance, of itself. A great real-world explanation of classes is in role-playing games where at the beginning you create your own character (and credit for this thought goes to the Invent With Python blogger). Your character will belong to a class—it'll be a Warrior, or a Druid, or a Healer, etc—but it will be unique to you, because it will have a name that you give it, and other information like special clothes that it wears because you decided on them, etc. Based on its class, your character will also have its own behaviours: it will be able to heal other characters if it's a Healer, or talk to plants if it's a Druid, or kick some serious butt if it's a Warrior, etc, and for the most part, these special behaviours will not be available to characters from other classes.
Similarly, a Ruby class has state—attributes like name, etc—and behaviours—methods that instances of itself can execute. We can think of attributes as things that describe the object and give it an identity from other instances of that class: it'll have its own name, for example. So attributes are things that that instance of the class IS, and its methods are things that it DOES.
When a copy of the class is taken to create a new instance of that class, it is given its own state/attributes. To illustrate, here's a Book class:
Books in the real world aren't meant to DO anything on their own (not that that stops them from being powerful forces...), but they are certainly meant to have unique attributes: their own title and author, for example. In the example above, the class Book is a set of code that, when a copy of itself is made and given a title and an author, will be a unique Book object.
The methods title
and author
expose the values
contained inside the instance variables @title
and @author
respectively. (Instance
variables are variables that are expressely there to hold values for a specific instance of that class: they are
available anywhere inside the class to give their value or to have that value altered.) Using attr_reader
,
we can abstract away those simple title
and author
methods into the following:
attr_reader :title, :author
, which under the hood is exactly what those little methods are, just
differently written. Here's the Book class updated to use attr_reader
:
Looks like the parts inside initialize
changed too! Like attr_reader
,
writing variable assignments like this is just a shorter, one-line way of doing the same thing as before.
Now let's make a Library class:
Our Library class is a little more complex than our Book class. Our Library class has methods that allow it to
DO stuff, not just BE stuff: it can add_book
into itself, it can suggest_a_book
to the
user, and it can suggest_an_author
too. Don't focus too much on the code inside add_book
just yet, we'll get to it in a moment. Right now, let's make a new Library object, and ask it to show itself to us
using p
:
That's a very interesting bit of code right there in the comments: when we asked my_library
to show itself to us, it printed out that long string of characters. The first part is clear: it's telling us
what class it belongs to. The next bit of numbers is its object ID; and that last part, the @books=[]
, is its
attribute books
, which we said is empty when we first initialize our Library. (Since
all libraries start out empty at first and get filled up with books over time.) So now, let's add some books
to our library using the add_book
method:
So before, our library had an empty array for @books
attribute: but now it's filled with 4 Book objects, each
with their own attributes of @title
and @author
! This is because the Library method add_book
takes two arguments, title and author, and uses those arguments to create a new Book object each time it is called;
then it "shovels" the new Book object as a whole—including its object ID and everything else about it, like its
attributes—into the @books
array.
It's a bit hard to read those long indecipherable object strings. Let's ask it to show us its books in a nicer way:
That's much nicer! @books
holds the value of an array of objects, and the array method each
allows us to iterate over @books
and ask each book object to put itself to the console in the way we've
specified.
Now let's try out its rather pushy suggest_a_book
and suggest_an_author
methods:
Super nifty! That's all for today, I'm feeling like opening up my copy of The Well Grounded Rubyist now.