Let's zoom away from the instance level and back to the class level for a second. Just like there are instance variables and instance methods, you can also use class variables and class methods. These are behaviors that are so general that there's no reason to apply them to each and every instance of the class. The rule of thumb for deciding whether something should be a "class var/method" or "instance var/method" is really by asking the question "Does this actually care about the internal state of a specific object?" You'll see this in action below.
In this lesson, we'll show you how you can implement these and why you might find them useful. In OOP, class methods especially will pop up quite a bit (though not nearly as much as instance methods).
Class variables, denoted with TWO
@@'s, are owned by the class itself so there is only one of them shared among all the instances instead of one per instance. They aren't incredibly common but there are some elements of state that are obviously meant to be shared across all instances.
In this example, we assume that all Vikings start with the same health, so we don't make it a parameter you can pass in:
class Viking @@starting_health = 100 def initialize(name, age, strength) @health = @@starting_health # ...other stuff end end
If you're thinking that class variables seem pretty similar to constants, you're only correct in one fashion. They are similar because all instances have access to them but that's where the similarity stops. The key difference is that class variables can change while constants cannot.
If you've got something that will never and can never change, use a constant. If you might ever change it, stick with a class variable.
What if you want to set up a method that represents general functionality and doesn't actually need the details of a specific instance to run? Sounds like a case for a class method.
You can define a class method one of two ways:
def Viking.class_method) since that's what
# Method 1: (most common) class Viking def self.count_all # code to count up all Vikings end end # Method 2: (less common) class Viking def Viking.count_all # code to count up all Vikings end end # IRB > Viking.count_all #=> 123
There are two common situations when a class method makes perfect sense. These are certainly not the only ones, as you'll see when we dive into Rails.
Though we've come up with fancy names to describe these types of class methods, they're really just patterns of use and not some sort of official Ruby syntax.
The first case is commonly called a Factory Method, and is designed to save you from having to keep passing a bunch of parameters manually to your
In this example, we've identified a special kind of Viking called a "warrior" which has lots of preset values. If we're going to be creating lots of warrior Vikings, farmer Vikings, merchant Vikings etc. and each of them has a pretty defined set of preset attributes, it saves us some trouble to just make a helper (factory) method for creating each of these.
Here, we've set up a
create_warrior factory method which runs our
Viking.new method with a set of randomized preset values:
class Viking def initialize(name, health, age, strength) # code to initialize end # Randomness makes for more interesting variations # between warriors def self.create_warrior(name) age = rand * 20 + 15 # remember, rand gives a random 0 to 1 health = [age * 5, 120].min strength = [age / 2, 10].min Viking.new(name, health, age, strength) # returned end end # IRB > sten = Viking.create_warrior("Sten") #=> #<Viking:0x007ffc05a79848 @age=21.388120526202737, @name="Sten", @health=106.94060263101369, @strength=10>
Note that it's pretty handy of IRB to list out the instance variables for you after you've created a class. It's almost identical to the output if you were to type
inspect outputs a string so it escapes any quote characters within it (visible in the
> sten.inspect #=> "#<Viking:0x007ffc05a79848 @age=21.388120526202737, @name=\"Sten\", @health=106.94060263101369, @strength=10>"
The second case for class methods is more mundane. Often, there are things you need all Vikings to "know" or be able to do:
class Viking ... def self.random_name # useful for making new warriors! ["Erik","Lars","Leif"].sample end def self.silver_to_gold(silver_pieces) silver_pieces / 10 end class << self # The less common way def gold_to_silver(gold_pieces) gold_pieces * 10 end end end # IRB > warrior1 = Viking.create_warrior(Viking.random_name) #=> #<Viking:0x007ffc05a745c8 @age=22.369775138257097, @name="Lars", @health=111.84887569128549, @strength=10>
This is also potentially useful for class-wide operations like the
Viking.count_all function we used previously.
Again, this is where the rule of thumb comes in. If you find yourself writing a method that doesn't actually use any unique state from a particular object (its instance variables), it should probably be a class method not an instance method.
It's worth noting that class methods, because they're called by a class and not an instance, obviously cannot access any instance variables or instance methods. This should make sense since, when you call a class method, that method has no way of knowing which instance you might be referring to.
If you wanted to, you could pass an instance as an input to a class method (e.g.
Viking.send_to_army(some_viking_instance)) but you'd have to ask yourself whether it would be better as an instance method in that case.
Class methods can access other class methods.
Though class methods cannot access instance variables, instance methods can access both class methods and class variables. This should make sense because, of course, an instance of a class will be able to "see" everything that's class-wide.
The important bits of code from this lesson
# Class variable (accessible across instances) @@starting_health = 100 # Class method def self.count_all # code to count up all Vikings end # Call a class method Viking.count_all
Most of your time in OOP will be spent wrangling classes, instance variables and instance methods. The class methods (and occasionally class variables) are often more of a supporting player in that process because they tend to rep more general functionalities. As we dig deeper into OOP, though, you'll find plenty of occasion to use all of these.
One of the key things to understand is the difference between instance variables and methods and class variables and methods. If you've got that, then you have a good understanding of what's going on here. If not, go back and re-read the last few lessons before moving on.