Classes
Tags: ruby, webdev, programming
Published: 2023-08-22 18:08:02

## Introduction

Classes is a concept available in most programming languages. We use classes from the very beginning of learning any language but it is often the hardest concept to wrap your head around. When I first learned to create an array in Ruby, I did as follows:

```ruby
array_name = Array.new(x,y)
```

X and y are optional parameters. X represents the length of the array and y represents the content of the array. Even though it was glossed over and we eventually moved to using a literal constructor. It was nice to see that our arrays in ruby come from the Array Class proving that they are everywhere.

## Declaring a class

```ruby
Class ParentClass
  @@number_of_instances = 0
  def initialize(name, id)
    @name = name
    @id = id
    @@number_of_instances += 1
  end
end
```

Above you can see that Classes are defined with the keyword class followed by the name of the class capitalized.

Classes also provide a special method named initialize which is a type of method that is only executed when a new instance is created using the .new() method and passing in the parameters. As you can see inside initialize, we need to use a instance variables, so it is persistent on each instance even if it is different value. If we do not declare the variables as instance variables, we will get a nil error.

I also declared a class variable that gets incremented by one each time that a new instance is created to keep track of the number of instances.

## Declaring your own methods

```ruby
Class ParentClass
  @@number_of_instances = 0
  def initialize(name, id)
    @name = name
    @id = id
    @@number_of_instances += 1
  end
  def greet
    puts "Hello World!"
  end
  def self.instance_count()
    puts @@number_of_instances
  end
end
```

Here we have two examples, a class method and an instance method.

We declared an instance method named greet. This prints "Hello world!" when it is called. This method persists through every instance of the class. Such as instance_name.greet.

We also declared a class method. This class prints the number of instances of the Class and it is defined using def self.method_name . A class method gets called on the class itself.

## Getter and setter methods

In most languages, you need to define your own getter and setter methods. These methods are used to get or update the value of a variable outside the encapsulating class. Ruby also requires this; however, they have a shortcut.

Introducing attr_:

```ruby
Class ParentClass
  @@number_of_instances = 0
  attr_accessor :name
  attr_reader :id
  def initialize(name, id)
    @name = name
    @id = id
    @@number_of_instances += 1
  end
  def greet
    puts "Hello World!"
  end
  def self.instance_count()
    puts @@number_of_instances
  end
end
```

We have three types: attr_accessor to create getter and setters for a given variable, attr_reader to create a getter, and attr_writer to create a setter.

Above we used attr_accessor :name to create a getter and setter method for the instance variable @name and attr_accessor :id to create a getter for the instance variable @id. Now if we create an instance of our class and we want to modify the name, we can as follows:

```ruby
aldo = ParentClass.new("Waldo", 1234)
puts aldo.name # => "Waldo"
aldo.name = "Aldo"
puts aldo.name # => "Aldo"
```

Here we can see we made a typo when creating the instance, we were able to use the getter to initially print the typo and use the setter to modify it using dot notation. God bless getters and setters.

If you are dying to see how to declare them without using attr_... within the Class write as follows:

```ruby
def name
    @name
  end

  def name=(str)
    @name = str
  end
```

It's definitely a lot of work especially when you start inheriting from your class and have more instance variables. Look at it as syntactic sugar.

## Inheritance

```ruby
class ChildClass < ParentClass
  ...
  def greet
    puts "Hello from the Child Class"
  end
end
```

Here we derived a class from our base class using <. The Child Class now has access to everything in our parent class. In our child class we did define a method named greet, and it will override the base class method unless we do not declare the method at all or use super(). We also did not declare an initialize to further show the power of super().

```ruby
class ChildClass < ParentClass
  def initialize(name, id, favorite_food)
    super(name, id)
    @favorite_food = favorite_food
  end
  def greet
    puts "From the Child Class: "
    super()
  end
end
```

This will now print "From the Child Class: " followed by "Hello World!" which gets printed from the Parent Class.

We now rendered our initialize method. Notice how in the parameters we passed name and id. Didn't we define those in the Parent Class already? Yes but no. The () in initialize is the way the ChildClass will accept parameters. If we do not give it a way to accept name and id, they will be undefined or equal the default value.

Well do we still need to recreate the instance variables? This is when super() comes into play yet again. We just pass in the parameters that already have an instance variable in out parent class into super(), and they are now defined as part of the ChildClass and can be accessed within.