2.0 Instance Variables and Accessors

Instance Variables

We cover instance variables briefly in The RubyMonk Ruby Primer: Building your own class lesson.

There really isn't much more to instance variables. They are bound to an instance of a class and together forms what we call the state of an object. Every instance of a class has a different set of instance variables. Let us see how that works out:

Example Code:

Output Window

As you can see from the above example, Item.new creates a new object - an instance of the class Item. The instance variables, in the example above, @item_name and @quantity, are prefixed by the @ symbol. This is enforced by Ruby - if your variable does not start with a @, it is considered to be a local variable.

What is the difference between local and instance variables? I'll demonstrate by an example. Note that the following example is going to raise an error and that is fine.

Example Code:

Output Window

The error message should be instructive on how the local variable supplier operates. A local variable is available only inside the method it is defined. It is not shared across the entire object. In programming language parlance, we say that the 'scope' of a local variable is bound to the method where it is defined.

Contrast the scope of a local variable with that of an instance variable: the instance variable is bound to the specific instance of the class. By binding itself to the entire object, an instance variable makes itself available to every method of the object.

Getter and Setter methods

An object can't exist in isolation. It has to communicate its state to other objects at some point. However, only the object's own methods can access its instance variables. So, you can have methods known as 'getter' methods whose sole purpose is to return the value of a particular instance variable. Let us see an example:

Example Code:

Output Window

The getter method here is the item_name method. See, that was simple.

Having to explicitly define getter methods ensures that the object is always in control of how your state is exposed to the public. Let me give you a simple exercise to demonstrate why it is a great thing.

We hark back on our Item class yet again for this exercise. The Item class has a new field: color. But the fact that we store the color as a separate attribute inside our object should not affect how the outside world consumes our object. In this exercise, you have to define a description method, a getter, which returns a string that has both the description and color of the object.

Output Window

So far we talked about accessing the contents of an instance variable through getter methods. Now, how do you change the value of the instance variables from outside? That is where setter methods come into play.

To execute the statement item.color = 'red', Ruby invokes the the method Item#color= and passes the value 'red' to it. This means that all setter methods end with the = sign in their names. For instance, the method color=.

In the following exercise, I've defined a stub for the setter method quantity=. You have to implement it so that the instance variable @quantity is set to the parameter new_quantity.

Output Window

Making life easy through attr_accessors

Ruby provides a couple of methods to make life easy when declaring getters and setters for your object. Let us start with an example of the attr_reader method:

Example Code:

Output Window

The attr_reader method defines the reader method for you. This is a convenient shortcut that you can use when your getter simply returns the value of the variable of the same name.

There is a similar method attr_writer which, as you may expect, defines a setter method that sets the value of the instance variable of the same name as the setter. Why don't you try using attr_writer to implement a setter for description and color in the following exercise?

Output Window

Now you know what the getter and setter methods are, and how you can use the attr_reader and attr_writer methods as a shortcut for defining them.

In some cases you might want to expose both the getter and setter for an instance variable. Instead of having to call attr_reader and attr_writer for the same variable, you can use another method, the attr_accessor, which will define both the getter and setter.

To summarize:

  • attr_reader :description will define def description; @description; end
  • attr_writer :description gives you def description=(new_description); @description = new_description; end
  • and
  • attr_accessor :description is equivalent to attr_reader :description; attr_writer :description

Ruby restricting access to instance variables except through getters and setters is not a limitation of the language, but a deliberate constraint. This is imposed so that Ruby code follows object oriented best practices. Getters and setters are in fact a way to get around this very limitation - which tells us that you should be a little cautious while using them. I won't go into the details here, but we will talk about it in our upcoming book 'Object Oriented Programming in Ruby'. However, I recommend you check out this article by Allen Holub that talks about object oriented programming and how getters and setters can be bad for your code.

Congratulations, guest!


% of the book completed

or

This lesson is Copyright © 2011-2014 by Jasim A Basheer