Sunday, October 20, 2019

Instance Variables in Ruby

Instance Variables in Ruby Instance variables begin with an at sign () and can be referenced only within class methods. They differ from local variables in that they dont exist within any particular scope. Instead, a similar variable table is stored for each instance of a class. Instance variables live within a class instance, so as long as that instance stays alive, so will the instance variables. Instance variables can be referenced in any method of that class. All methods of a class use the same instance variable table, as opposed to local variables where each method will have a different variable table. It is possible to access instance variables without first defining them, however. This will not raise an exception, but the variables value will be nil and a warning will be issued if youve run Ruby with the -w switch. This example demonstrates the use of instance variables. Note that the shebang contains the -w switch, which will print warnings should they occur. Also, note the incorrect usage outside of a method in the class scope. This is incorrect and discussed below. #!/usr/bin/env ruby -wclass TestClass # Incorrect! test monkey def initialize value 1337 end def print_value # OK puts value end def uninitialized # Technically OK, generates warning puts monkey endendt TestClass.newt.print_valuet.uninitialized Why is the test variable incorrect? This has to do with scope and how Ruby implements things. Within a method, the instance variable scope refers to the particular instance of that class. However, in the class scope (inside the class, but outside of any methods), the scope is the class instance scope. Ruby implements the class hierarchy by instantiating Class objects, so there is a second instance at play here. The first instance is an instance of the Class class, and this is where test will go. The second instance is the instantiation of TestClass, and this is where value will go. This gets a bit confusing, but just remember to never use instance_variables outside of methods. If you need class-wide storage, use class_variables, which can be used anywhere in the class scope (inside or outside of methods) and will behave the same. Accessors You normally cannot access instance variables from outside of an object. For instance, in the above example, you cannot simply call t.value or t.value to access the instance variable value. This would break the rules of encapsulation. This also applies to instances of child classes, they cannot access instance variables belonging to the parent class even though theyre technically the same type. So, in order to provide access to instance variables, accessor methods must be declared. The following example demonstrates how accessor methods can be written. However, note that Ruby provides a shortcut and that this example only exists to show you how the accessor methods work. Its generally not common to see accessor methods written in this way unless some sort of additional logic is needed for the accessor. #!/usr/bin/env rubyclass Student def initialize(name,age) name, age name, age end # Name reader, assume name cant change def name name end # Age reader and writer def age age end def age(age) age age endendalice Student.new(Alice, 17)# Its Alices birthdayalice.age 1puts Happy birthday #{alice.name}, \youre now #{alice.age} years old! The shortcuts make things a bit easier and more compact. There are three of these helper methods. They must be run in the class scope (inside the class but outside of any methods), and will dynamically define methods much like the methods defined in the above example. Theres no magic going on here, and they look like language keywords, but they really are just dynamically defining methods. Also, these accessors typically go at the top of the class. That gives the reader an instant overview of which member variables will be available outside the class or to child classes. There are three of these accessor methods. They each take a list of symbols describing the instance variables to be accessed. attr_reader - Define reader methods, such as the name method in the above example.attr_writer - Define writer methods such as the age method in the above example.attr_accessor - Define both reader and writer methods. #!/usr/bin/env rubyclass Student attr_reader :name attr_accessor :age def initialize(name,age) name, age name, age endendalice Student.new(Alice, 17)# Its Alices birthdayalice.age 1puts Happy birthday #{alice.name}, \youre now #{alice.age} years old! When to use Instance Variables Now that you know what instance variables are, when do you use them? Instance variables should be used when they represent the state of the object. A students name and age, their grades, etc. They shouldnt be used for temporary storage, thats what local variables are for. However, they could possibly be used for temporary storage between method calls for multi-stage computations. However if youre doing this, you may want to rethink your method composition and make these variables into method parameters instead.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.