Classes in object oriented programming are the heart of the programming paradigm. Without them, we could not mimic objects in the real world as we currently do in programming. By the time you are finished with this article, you should have a solid or much improved understanding of what classes are and how they work in the object oriented paradigm.
Classes are software’s way of imitating an object. Let’s think about any object. It can either perform actions or be configured to your liking. So if we have a car as an object for example, it can be a certain color and have a certain horsepower, and it can perform actions like drive, brake, turn on windshield wipers and so forth.
So using the car as the example, we’d want to create a class (or blueprint for how the object is made). This is very similar to a blueprint for a building or highway intersection. We will also want to add attributes that are important to our object. Of course, you can go into great detail or keep it shallow depending on the needs of your program or your capabilities.
So for our car, we’ll keep it somewhat shallow and discuss the software principle containment. We’ll have attributes such as color, make, model, horsepower. Actually, this could be much easier done by going to a car website and configuring a new car. All those things you can configure are practically the same as the attributes you’d have in your car. Now the car can accelerate, maintain speed (cruise), decelerate, turn and so forth. So we’ll want to have these as behaviors of the car. Note, you only want the behaviors that you will support or are important to your program. Just because you can put a behavior in your class, doesn’t necessarily mean you should.
Alright, now let’s get to the nitty gritty. There are a few concepts that must be understood to truly get the benefits of OOP. First is how to create the object. This is done with the constructor. In the constructor, we put anything that initializes the state of object when it is created especially if we don’t want to use setters and getters all the time. We also have the destructor, which is called right before the object is destroyed. If you understand callbacks, the constructor is like a callback for object creation and the destructor is like a callback but prior to destruction.
Now let’s switch gears and use guitars as that is the main photograph for this article and we get to do more architecting 🙂 . So we look at the guitars and decide that we need a class so we can create Guitar objects. Our end goal is to have a person play the Guitar in software.
Ok so first we look for possible attributes. What can we say about the guitar? It has a color, a number of strings, is it electric or not and any other differences an expert guitarist might observe. So we add these variables as attributes.
//pseudocode
Class Guitar
int color;
int num_strings; //how many strings - for maintenance - int
bool is_electric; //is it electric? Boolean
Ok, so we’ve got some attributes that will tell us about the guitar. Now we want to think about the behaviors or functionality of the guitar. To do this, we will create 2 other objects since on its own, I can’t think of too many behaviors for the guitar. So we want to create a Person object. The person will play the guitar (which the guitar cannot do on its own traditionally anyways). So the person is not too important, they just need to be able to pick up a pick and strum the guitar. We will want the name of the person so we can display what Person object is playing the guitar.
Class Person
int age; //not needed, we don’t care for this project
string name; //who is playing the guitar
So for containment. The idea is the person will pick up the pick and guitar and play it. So the person needs to have a way to contain a pick object and a guitar object. We will also need a song (class) that will be played, otherwise, we’d have no clue how to play the guitar. Let’s create those.
Class Person
.
.
.
Pick * picklist; //holds the picks the user contains
Guitar * guitar; //holds the current guitar
Song * song; //song guitarist will play .. could be a string also based on design
We might also make an electric guitar that has the characteristics of a normal guitar but is electric. I do not know enough about guitars to give exact information, but bear with me and we’ll bring it all together.
Class ElectricGuitar extends Guitar {
//inherits from Guitar
//automatically has variables color, num_strings, is_electric
//can add more specific attributes and functions for this class here
}
//we can add things specific to guitar as additional attributes and functions
Class Pick
int color; //create a pick class and give it a color
So now we have most of the objects for a person playing a guitar. Note as in the real world, we have a class for each object (Object Oriented Programming OOP). We’ll need to instantiate those classes and produce actual objects, but we have a blueprint for how we want to do that based on the classes we created for our software. So we know we want a person to play the guitar. So that will be an action or function in the Person class. We may also verify in that function that the guitar has been picked up already, or we may require that this person picks up a Guitar when they are created. We can do this in the constructor, but for now let’s add the function to our class.
Class Person
.
.
.
playGuitar()
Since we are using OOP, we don’t actually need to pass a guitar to the function, we can just check the one contained within the Person object, since we designed it that way. You could also require that the Guitar be passed to the function by adding a parameter for the guitar. For now, we will just use OOP and check if the person has a guitar. If we wanted to force a guitar to be picked up by each user (Software assumes the person has a guitar already), we’d use the constructor.
Class Person
.
.
construct(Guitar g, Pick p) {
this.guitar = g; //assign the guitar given to function to our object
this.pick = p; //assign the pick passed to the function to our pick
this.song = getRandomSong();
}
function playGuitar(){
if (guitar == NULL){
//user does not have a guitar
//force user to pick up a guitar from random list or exit function
this.guitar = getRandomGuitar();
}
if (pick == NULL){
//same choices, usually getters and setters would set these variables after object creation
this.pick = getRandomPick();
}
//now we know that the user has guitar and pick
//let’s play the guitar
guitar.play(this, song); //pass this object to the play function of Guitar class
}
//Let’s see how the play function in the Guitar class might look since that
//is doing the heavy lifting in the playGuitar function in the Person class
Class Guitar{
function play(Person * p, Song * music){
//play the guitar according to the song
//move person p's fingers and the guitar's strings to music melody
//to play, we need to move the strings and set the position so it looks like it is being played
//insert those details here based on design choices, implementation, and so forth
}
}
Alright, that should possibly be good enough at this time, we can always refine what is needed, which you will often do. At the moment this is like creating a rough draft. So let’s write a main routine and start using this stuff to see how classes and objects work together to aid in your program.
function main
Person you = new Person(); //if constructor requires a guitar, you’d need to create a guitar and pick and pass them to constructor
Person me = new Person();
Pick apick = new Pick();
Guitar my_guitar = new Guitar();
Guitar you_guitar = new ElectricGuitar(); //Electric guitar is a type of guitar so it can be stored as a Guitar (for polymorphism sake)
So now we’ve created a bunch of objects, hopefully now you’ve seen the differences between objects and classes. In the above snippet, the classes are always the same and usually have Class to signify it is a class. The classes are on the left (variable type) and each object is to the right (object name for this type). So the class is like a blueprint, however, the objects, are actual creations or instances of the class. So if you have a blueprint, you’d use the blueprint to create the real object. The classes we created above are Person, Pick and Guitar (Song is left as an exercise 🙂 ). The objects however are you of type Person, me of type Person, apick of type Pick, my_guitar of type Guitar, you_guitar of type ElectricGuitar. We have 3 classes and 5 objects. We can create many more objects from our blueprints, but in this case we’ve only created 5 objects of 3 different types – ElectricGuitar is of type Guitar through inheritance.
So we have a class that serves as the blueprint, but just as in the real world, two objects are independent of each other although they may have the same features or blueprint that they were created from. Such as two people. They are both people (of the Person class) but what happens to one person does not affect the other. You might say the constructor of a person in the real world is giving birth. This creates the person with certain hair color, DNA characteristics, physique and so on. But then we need to fast forward so the person can do things (as we can do in programming 🙂 ) So in the main above, we have you Person and me Person. Any modifications we make to you will not affect me (unless globals and shared memory is used and it is designed that way). Same with the two Guitars, my_guitar is a completely different object than you_guitar, however, they are both of type Guitar (not exactly in this case, since more specifically one is of type ElectricGuitar which is of type Guitar). Ok, now that we have the objects we need, we want to have the person pick up a guitar and pick. So we will write something like
me.setGuitar(my_guitar);
you.setGuitar(you_guitar);
me.setPick(aPick); //pick up the aPick that was a created object
you.setPick(); //pick up a random pick
me.setSong(‘My favorite country song’);
you.setSong(‘My favorite rock song’);
Note these look just like setters and getters. Yet we did not think of setters and getters, we just wanted to have the person be able to obtain a guitar and pick. If you think about why different concepts in programming exist, it can deepen your understanding quite a bit. Just as in the real world, someone complains and the company takes steps to address the complaints, writers of programs also complain to which the program creator must also address.
Ok, so now we have the guitar set, which for our program means the person has picked up the the guitar. For a little more practicality, let’s assume a row of guitars is shown to a user and the user selects a guitar by touching it. After it is touched, we will get the guitar they touched and call a function very similar to setGuitar etc.
Now we suggested that we want the person to play the guitar, which will then call play on the guitar objects. This is what is meant by implementation. Whatever method you choose to show a guitar playing is what you would drop into this function. Note you can easily switch out how it is played without affecting the rest of the program (abstraction). It’s pretty cool if you have an understanding of this last concept especially if compared to functional programming.
me.playGuitar(); //will play my_guitar with person me
you.playGuitar(); //will play electric guitar you_guitar with person you
–Revision made while writing this example — So after looking at the prior calls, I realized you’d want to play a song. And Songs are different. They have different tunes and different lengths, so it would make sense to have a Song class and set the song prior to playing the guitar. This also belongs to the Person class since the person picks the song, although some guitars have songs that they can play internally (designed that way). To do that, you’d just rearrange variables and what object owns what. This is the essence of design. There are in essence infinite ways to design a solution to a problem, so knowing the trade offs and such of each decision you make can be very beneficial to the end result.
In the above example, we randomize a song in the constructor, however, we could also check if the song has been set when playGuitar is called and randomize if not like we did the other variables. Since we have separate objects all created from the same classes, we know that when me.playGuitar() is called, the person me will play a Guitar with the favorite country song. And when you.playGuitar() is called, the person will play an Electric Guitar with the favorite rock song. It is very important to understand the previous sentence so please take your time and possibly re-read above until the previous sentence makes sense to you. So to recap, we have 1 class from which we created 2 independent objects. Based on how those objects are configured, they will act/play accordingly.
Same concepts .. Real World Exercise
So one last thing to ensure we understand objects and classes. There are many additional tools in programming languages, but they really just stem from the real world and things us programmers need to make programming more secure and less error prone. So to ensure understanding, let’s actually produce this post image in a program. First we want to create all these guitars. So we have to assume we have a class named Guitar. So our main function will look something like this.
function main
//create the guitars
Guitar guitar8323 = new Guitar();
Guitar guitar8326 = new Guitar();
Guitar guitar8353 = new Guitar();
Guitar guitar8366 = new ElectricGuitar();
Guitar guitar8392 = new Guitar();
Guitar guitar8357 = new Guitar();
Guitar guitar8391 = new ElectricGuitar();
Guitar guitar8367 = new ElectricGuitar();
Guitar guitar8301 = new Guitar();
Now we’ve created the guitars, we need some way of saying that we have these guitars and they are available. So seems like a list will make the most sense here, of course you can use an array and so on based on what is important to your program.
list<Guitar> my_list;
my_list.add(guitar8323); //add guitar to our list
my_list.add(guitar8326); //add guitar to our list
my_list.add(guitar8353); //add guitar to our list
my_list.add(guitar8366); //add electric guitar to our list
my_list.add(guitar8392); //add guitar to our list
my_list.add(guitar8357); //add guitar to our list
my_list.add(guitar8391); //add electric guitar to our list
my_list.add(guitar8367); //add electric guitar to our list
my_list.add(guitar8301); //add guitar to our list
Now we have all the guitars in a list, just like the picture. And just like the picture, we can sell these guitars, so we’ll want to display our list first, then the user can make a choice, and when they buy it (pay us) we’ll want to remove it from the list since it is no longer available, just as you would remove the guitar and hand it to the buyer. In both cases, the guitar is no longer available after being bought. So we’ll only look at one function, and you can create others in similar fashion through polymorphism.
Let’s assume the guitar class has a function called display. So we’ll want to call this function to display each object. So Electric guitar might display a little different or have some symbol on its display that signifies this is an electric guitar. Ok, let’s look at how polymorphism helps us in this case.
function showGuitars(list<Guitar> displayList)
//need to traverse the list and display each one
foreach (guitar in displayList){
guitar->display(); //display each guitar
}
So how does this work, well we know that an Electric Guitar is a type of guitar. So it may have more specific information that a normal guitar may not contain, but based on the fact it is inherited, it has to contain all the functions of the base class Guitar. Hence when we call Guitar->display .. polymorphism allows the program to choose the correct display function to call. For the guitars like guitar8323, it will call guitar->display, however for guitar8366, it will call electricguitar->display. All of this is done automatically through polymorphism. How cool!
As a quick aside, this is why functions can take interfaces. Interfaces guarantee that certain functions are implemented. As long whatever object the function is called on supports that function, then it does not really matter what the object is, it only matters that the object can perform the task. For example, let’s say there’s a function fight. We can call human->fight, bird->fight, bear->fight, penguin->fight and so on. These are all different objects (some mammals and some not) with different hierarchical structures, yet they all support the fight function. So as long as they all implement the interface that requires fight be implemented, then the function can take anything that supports that interface and the right function can be called .. more cool.
To finally recap on this ultimate guide, we’ve went in depth on objects and their construction. A little bit on class hierarchy and brushed on the pillars of object oriented programming while discussing classes and objects. We took a nice photograph and in essence programmed the photograph for sale. We also touched on some of the other related subjects like interfaces. Hopefully you have a firm understanding of object oriented programming from this ultimate guide. Good luck!