Object Oriented Programming- Self
Tags: ruby, beginners, programming, tutorial
Published: 2023-07-27 20:21:15

When it comes to object-oriented programming, the concept of "self" can be quite intimidating, especially for those who are new to the programming world. For today's discussion, I will try to explain the concept of "self" with respect to the Ruby programming language and how it works.

## Introduction to Self

One could think of **self** as having "self-awareness" when it comes to what code should be executed at any given time. When we create a new instance of a class, that instance is considered to be an **object** of that class that contains data and behaviors for that object.

For example, if we have a Student class defined as:

```
class Student
   
   attr_accessor :name
   
   def initialize(name)
     @name = name
   end

   def intro
    "Hi my name is #{name}"
   end
end
```

And then we create our new Student instance:

```
tim = Student.new("Tim")
```

We can access Tim's name by:

```
tim.name
# => "Tim"
```

Or we can let Tim introduce himself:

```
tim.intro
# => "Hi my name is Tim"
```

## Using **Self** in an Instance Method

So... does Tim know that he is a student? This is where the concept of "self-awareness" comes into play. Since we created Tim as an instance of Student, is there anyway that Tim will know that he is a Student? One way we can find out is to ask him!

```
class Student
   def show_self
     puts self
   end
end
```

```
tim = Student.new
```

```
tim.show_self
# > Student: 0x00007f81b3924bb8>
```

What is the keyword **self** doing here? Self is referring to the **instance** that the '**show_self**' method is being called on, which is the Student class. Better yet, Tim is asking "What am I?" In which the answer is a Student instance.

## Expanding our Code

How do we add in Tim's teacher? Let's see how we can incorporate that into our code. 

```
class Student
   
   attr_accessor :name, :teacher
   
   def initialize(name)
     @name = name
   end

   def intro
    "Hi my name is #{name}"
   end
end

```

Since we incorporated the teacher attribute in our attr_accessor (getter and setter method macro) we can set Tim's teacher by:

```
tim.teacher = "Professor Black"
```

And then call Tim's teacher: 

```
tim.teacher
# => "Professor Black"
```

This is great! But what if Tim switches schools or gets a new teacher halfway through the year? How will our Student class be able to handle this? 

```
def new_school(student, new_teacher)
   student.teacher = new_teacher
end
```
Here we have a method that takes in two arguments, an instance of Student(Tim) and the new teacher's name. From here we can have Tim update his new teacher:

```
new_school(tim, "Professor McGonagall")

tim.teacher 
# => "Professor McGonagall"
```

This process will work; however, with the use of **self** we can make this process a little cleaner. 

```
class Student
   
   attr_accessor :name, :teacher
   
   def initialize(name)
     @name = name
   end

   def intro
    "Hi my name is #{name}"
   end

  def new_school(new_teacher)
   self.teacher = new_teacher
  end
end
```
Here, the use of **self** refers to whatever Student instance the '**new_school**' method is being called on. In our example, self is referring to Tim and the new_teacher is going to be value we assign to this attribute. 

Here's how we can use the '**new_school**' method:

```
tim = Student.new("Tim")
tim.new_school("Professor McGonagall")
tim.teacher
 # => "Professor McGonagall"
```

In conclusion, understanding the concept of **self** in object-oriented programming is fundamental to becoming proficient in languages like Ruby. Embracing the notion of "self-awareness" empowers us to create dynamic and flexible code, enabling our objects to interact effectively and adapt to changing scenarios.

Happy Coding!