Understanding “Self” in Object-Oriented Programming (OOP)

Big Idea

Ruby is a nimble programming language because it is an object-oriented programming language. Abstraction gives the programmer the flexibility to be creative and poetic, telescoping in and out of repetitive methodological processes — loops, iterations, logical workflows — to get to the meat-and-potatoes of the problem.

Objects in Ruby are like complex versions of functions. They let you keep an “inside” full of variables and functions that revolve around an “archetype” or system of behaviors (like a Class or a Method). In Ruby, there is a keyword “self” that allows you to gather all the variables of that object around a single point of (self) reference.

Background

As a novice programmer, it is often difficult to understand abstract concepts like “Class” and Method,” which fail to have grounding in the 3D, physical world. In Ruby, objects have “self-reference,” meaning, objects understand what methods they are accessing at a given time. A Ruby object understands it is an object, complete with its own unique identity, blueprint, and object attributes.

This is fine and dandy. But how might we keep track of this metaphor “Ruby object?” What does this mean, when we are tallying method inputs and their outputs? How does “self” translate into hard code and what is the relationship of “self” in the context of its use in ruby classes and methods?

In Ruby, “self” gives us access to the current scope of the object. This keyword is the combination of the input data and method(s) of our object, and it changes according to what “self” is referencing. In a nutshell, “self” is understood as “current scope.”

Deconstructing “self” requires an understanding of what these abstract inputs, classes, and methods are doing in order to know what “self” is referring to. Adapted from “Hackhands” and reproduced below, we see “The Golden Three Rules of Self” which will assume 9 out of 10 all applications of “self” in Ruby.

self

Breaking Things Down

In programming, we decompose a problem into smaller problems so it becomes easier to solve. We make sure we have an “inside” and “outside” to a sub-problem so the “outside” can worry about what it needs from the sub-problem and the “inside” can worry about how to solve the sub-problem.

A function is a way of naming a subproblem, passing it some specifics, and letting the inside come up with the answer. Functions are good for a couple of variables. But what about a complex data structure like the one below?

scary data structure

Scary, I know. The above example needs an object which holds a bunch of variables and a bunch of methods. The variables in an object are related because they describe an object — or at least something that is convenient for us to conceive of as an object. Allow me to illustrate with an example.

Boxes of Dogs

Say you are building a program to retrieve information on Dogs. Picture this: you are in a room with four separate boxes. Inside each of the four boxes is a different type of dog: a Beagle, a Poodle, a Pug, and a Bull Dog. Every “box machine” is the same but every dog inside the box machine is different, and the dog inside each “box machine” represents the program data. On each of the boxes are method buttons, “dog_name”, “dog_weight”, “dog_poops.” When you put a Dog in a box and when a box_button is pushed, the box_machine gives you information about that dog. For example, if the dog poops, a button will be able to tell you how many poops that dog pooped, even though you can’t see the poops. 

The object definition — the code you use to define an archetype — is different from a dog instance, a specific version of that object.

Dog Example

In the above context, self is what the method say_hello calls on, “Dog.” “Dog” is the object and “Koko” is the instance of the object, which is another object. An object definition is like a box_machine that has buttons. When you push the say_hello button, the Dog box_machine performs a calculation and spits out information of that modified Dog object.

Dog Example (IRB)

When you create the above Dog_Box definition you use “self” to reference information about the Dog. You might use “self.name” or “self.age” or “self.number_of_poops” to obtain information about each of the Dogs. Remember: “self” is the box’s way of getting information about the Dog whereas Dog is the data that fills the Dog’s variables and attributes.

Self is a fundamental concept to understanding object-oriented programming because, as programmers, we put things “inside” of a function to decompose a problem or operation. Accessing the function’s name with “Self” is easier than dealing with the whole big chunk of code (block) every time you perform a calculation.