Common classes in Ruby include objects such as strings, integers and arrays. We can also create new classes in Ruby.
Why use a Ruby class?
Classes are useful to help organize objects with similar attributes and methods. Instead of defining multiple objects with similar attributes, we can instead define a single class and create instances of that class.
Defining a Class
A Ruby class is defined simply by class followed by the class name, written in CamelCase. Follow this by end and you have created a new class:
class Animals end
Creating Class Objects
Objects can be thought of as members or instances of the class. For example, we could create new objects/instances of the Animals class with the following:
pig = Animals.new sheep = Animals.new elephant = Animals.new
Pig, sheep and elephant are now objects of the class Animals.
If we want each object of the Animals class to have certain attributes, we can initialize the class using an initialize method. The initialize method will then pass the attributes of the class to .new:
class Animals @@no_of_animals = 0 def initialize(name, color=nil) @name = name @color = color end end
Two things to note about the above code:
(1) The initialize method takes two arguments, called parameters: name and color. In order to accept a new object by just its name, the color parameter is set to nil, which makes it an optional parameter. This means that to create a new object of the class Animals, we would need to pass in at least the parameter name and, if we choose, the parameter color. pig = Animals.new(“Wilbur”) would be sufficient, and the following would also work: pig = Animals.new(“Wilbur”, “pink”).
(2) @name and @color are instance variables whose values are set to the parameters of the initialize method. @@no_of_animals is a class variable which will keep track of how many animals there are in the class. It is important to know the difference between variable types when working with classes.
local – A local variable is defined inside a method and is unavailable outside the method.
@instance – An instance variable (declared with @) is available across all methods for a particular instance or object and can be applied to each object of the class. Instance variables exist as long as the object exists.
@@class – Class variables (declared with @@) are available across different objects of the same class. There is only one class variable throughout the class, so these variables are often used for counting or keeping track of how many instances of a class have been created.
$global – Global variables (declared with $) are available across all classes and can be modified from anywhere in the program. These should be used sparingly, if at all.
Getter & Setter Methods
“Getter” and “setter” methods allow us to access Ruby variables inside a class. In our Animals class, if we wanted to access @name or @color, or set/modify the value of these variables, we would need methods as follows:
class Animals @@no_of_animals = 0 def initialize(name, color=nil) @name = name @color = color end def name # getter method @name end def color # getter method @color end def name=(value) # setter method @name = value end def color=(value) # setter method @color = value end end
As you can see, this requires quite a bit of code, especially when there is a more efficient way to accomplish this.
Attribute accessors are shorthand for getter and setter methods. They allow us to access and modify our instance variables by passing them to the attribute accessors as symbols. There are three types:
attr_reader is a method that replaces the “getter” method above. It takes any number of parameters and, for each parameter, defines a “getter” method that returns the instance of the variable with the same name. attr_reader :variable returns:
def variable @variable end
attr_writer is a method that replaces the “setter” method above. attr_writer :variable returns:
def variable=(value) @variable = value end
Finally, attr_accessor is both a getter and a setter, as it performs the functions of both the attr_reader and attr_writer methods. attr_accessor :variable is the same as:
attr_reader :variable attr_writer :variable
Using the class Animals, this streamlines our code:
class Animals attr_accessor :name attr_accessor :color @@no_of_animals = 0 def initialize(name, color=nil) @name = name @color = color end end
Now, we are able to read and write @name and @color without the tedious task of coding each method individually.
Sometimes we want to create a new class which has the same methods and attributes of a class we previously created. We can accomplish this through inheritance.
class New < Old end
In the above code, < is a symbol meaning “inherit from”, and class New would inherit the methods and attributes of class Old. Class New would be the subclass, and class Old would be the parent class, or superclass.
What happens if class New inherits a method from class Old that it cannot use? Or perhaps class New functions slightly differently from class Old, and we would need to create a method in class New to override a method in class Old. There is an easy solution to this.
For example, if class New inherited the following method from class Old:
def welcome return "Welcome to class Old!" end
Instead of creating a new method which would override this and hanging onto an old method which would never be used, we can simply modify the method as follows:
def welcome return "Welcome to class New!" end
The new method would override, or replace, the inherited method.
Sometimes, when working with a subclass, we may overwrite a method or attribute inherited from a parent or superclass. super allows us to directly access the attribute or method of the parent or superclass:
def welcome puts "Welcome to class New!" super end
Unlike other languages, Ruby only allows for one superclass per class. Attempting to inherit from more than one class will result in an error.
Public, Private & Protected Methods
In Ruby, a class’s instance methods are public by default. The initialize method is private by default. If you want to make a method private, you simply declare private above the method. This makes the particular method private to the object where it’s defined, and it can only be called from other code inside that object. Protected methods are designated by declaring protected above the method, and can be invoked only by objects of the defining class and subclasses.
Instance vs. Class Methods
Each of the methods in the above examples is an instance method. Instance methods reside at the object level, whereas class methods reside at the class level. Simply creating a method inside a class creates an instance method. To create a class method, you would have to either use the class name:
class Animals def Animals.welcome puts "welcome is now a class method" end end
Or you could use self:
class Animals def self.welcome puts "welcome is now a class method" end end
self is the preferred method among Ruby developers, as it does not repeat the class name in the method (which would violate DRY – Don’t Repeat Yourself). If the class name were to be changed later, you would also have to change the name inside each class method.
But what is self?
self is used to refer to the object which holds it. In the above example, the object holding self is the class Animal, so calling self.welcome calls a class method, or a method on the object holding self. Because, remember…
Although classes are used to organize objects with similar attributes and methods, a class is itself an object in Ruby.
- PM Career Story - April 28, 2022
- How to Transition into Product Management - December 26, 2017
- What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015