So far DBC has been a real rollercoaster. I feel like the more I learn about something, the more I realize I still don’t know.
Some of the concepts of object oriented programming seem really simple until you start to dig into them and then you realize that when they said everything is an object, they meant EVERYTHING is an object! There’s even a main object that you didn’t even know about! It’s just there! And some things are objects of objects! And some things are classes of classes! And classes are blueprints for creating objects! And the classes that create objects are also objects!
My current method of dealing with confusion while learning is to try and understand as much as possible, and if I come across something that I just can’t wrap my head around, I just accept it without completely understanding it and hope that it will all make sense later.
Sometimes that approach works and later on something clicks in my brain I go “OHhhhhhh! That’s what you were talking about! Okay, okay, I got this.”
Other times I realize that although I understand what a thing is, I still have no clue how or when to actually use it, which means I don’t really understand it and need to do some more studying.
That’s why today I started off reviewing class and instance variables, got into class and instance methods, took a walk through singletons and inheritance, and briefly peeked into design patterns and duck typing.
If you want to try to follow along with my studying, good luck! If you want to skip this blog post and come back when I’ve crocheted something or baked something, I totally don’t blame you!
In Ruby, a class is a blueprint for creating an object. It holds methods that tell the class what the object should do. It holds attributes that tell the class how the object should be.
If I created a class called Dog, I’m not actually creating a dog object, just laying out the design for what Dog should be like.
After I add the command Pepper = Dog.new, now I’ve created a dog object. Her name is Pepper and she has all of the attributes and methods that are included in the Dog blueprint. I could create 100 dog objects from this one class. Here’s what I mean:
Those variables that begin with @ in the initialize method are instance variables. They only exist as an instance of this class. Multiple objects belonging to the same class can have different attributes for each instance variable, for example, various Dog objects can have different @color attributes. Pepper’s @color is set to equal “brindle”. We could create a Dog called Bruce whose @color is set to equal “mostly black”.
You may have noticed that I set @stinky to have a default value of true. This means that the instance variable will equal true, unless we specifically enter a value to override the default (Bruce is pretty clean, so we’ll set his @stinky attribute to false).
The scope, or range of influence that something has, of an instance variable is limited to the individual object. Pepper’s color does not matter to Bruce’s color. Any attribute of the object Pepper is irrelevant to the object Bruce. Bruce can not change or alter Pepper’s color. The scope of an instance variable is confined to whatever object self refers to. Instance variables should be used to represent the state of self.*
Another thing about instance variables is that they can not be accessed outside of the class. If we try to call Bruce’s color, for example, it will raise an error. To get around this, we could create a method that prints an object’s color and then call that method on the object Bruce, but we can’t directly access @color outside of the class.
We can use attr_reader, attr_writer and attr_accessor to access our instance variables from outside of the class! Reader allows us to simply print the variable (which may return nil if the instance variable has no default value), writer allows us to write to that variable and accessor allows both. Let’s access the @color variable and then read it and write to it.
Now, what if we want some variables to act on all objects created from a class? For our dog class example, we could add @@num_of_legs as a class variable, so that every dog object we create will have the same number of legs.
My apologies to any tri-pawds out there for assuming that dogs have 4 legs!
Up until now, I’ve been using all instance methods. Inside a class, the def keyword will create a new instance method, when used without an explicit receiver. Instance methods can only be used on objects of the class they (the methods) are created in. I can’t create a new class called Cat, create an object Henry from the cat class and expect to access the methods that are in the Dog class. I get an error.
Henry is our neighbors’ outdoor cat who poops in our yard and stares into our windows, probably looking for the dogs. Bruce hates Henry. Pepper is usually oblivious to other animals that aren’t standing directly in front of her, so whenever she notices Henry she hates him too.
*In the context of a class, self refers to the current class, which is simply an instance of the class Class (is your head spinning yet?). Defining a method on self creates a class method.
Once you understand that classes are objects, this use of the self variable to define a class method starts to make sense. The common syntax for creating a class method in Ruby is self.method_name, although you could also use class << self.
See how we didn’t need to create a new Person object in order to use those methods? Remember that a class is a blueprint for creating objects, but a blueprint is an object in itself. Instance methods act on whatever object we’ve created with the class. Class methods act on the class object itself.
What about Singleton methods and classes?
We created a class filled with methods, and then create an object of that class. But then, later on, we created that class again filled with the same methods but with overriding information in those methods. Our old methods no longer work!
Click to visit Ruby Monk tutorial
To get around this, you can define methods that are only available for a specific object. These are called singleton methods.
And earlier when we defined a class method using the second syntax, class << self, we were actually defining a method inside the Class instance itself, which is referred to as the singleton class. It uses the self keyword to open a new context where the Class instance is held in self.
A singleton class is almost a class in many respects, except that it can not be instantiated, meaning that you can not create instances of a singleton class. You can’t do Singleton.new or initialize instance variables on a singleton class. Use a singleton class when only one instance of a class can exist.
Class and instance methods become very important when we start to talk about inheritance.
Inheritance allows a child class to take on the attributes and methods of a parent class. Any class variables or class methods will act on the child class as if they were the parent class. Instance variables can be passed from the parent class to the child class using the super keyword.
One of the exercises we did this week at DBC was to first create 7 empty classes called Animal, Mammal, Amphibian, Primate, Frog, Bat, and Chimpanzee, populate them with instance variables and then set up inheritance relationships. For example, any object created from the frog class will inherit the attributes and methods of the amphibian and animal classes.
That’s enough for right now. I can spend 3-4 hours studying but then I need to take a break, stretch and maybe eat a cookie before getting back to work. Brb, cookie break!
As always, if you see an error or if I got something wrong please SPEAK UP and post a comment! I’m still learning this stuff and might have misunderstood something or got some bad information! Thanks :)