Introduction to Object-Oriented Programming

Let’s get down to business. It’s time to start learning OOP.

--

It’s time to start exploring one of the most advanced topics in web development. In this article, we’ll introduce you to Object-oriented programming, or OOP. This isn’t an easy subject, but it’s totally worth your time.

The term OOP itself may suggest that objects play a key role in this approach to programming. It’s actually a little more complicated than that, but we’ll elaborate on that soon enough. First, let’s talk about OOP in general and the reasons behind its creation.

Procedural programming

Typically, when people talk about traditional programming, they have procedural programming in mind, which is based on procedures and functions. A function is a mini-program that receives some data as an input, processes it, and eventually outputs some data. We can compare a function to a little factory inside of a black box.

For example, in an online store, there might be a function somewhere that checks the validity of an entered email address, which we’ll call verifyEmail() After all, it’s important that users enter valid email addresses so that they deal with any orders they might purchase. This email function should receive some text as its input, check the data against some internal rules, and then give back a result that says whether or not a valid email address has been entered. If it is valid, we’ll get back a value of true, and if not, the result will be false.

Functions are useful when you need to wrap up multiple commands into one package, and we could add any number of additional commands to our verifyEmail() function. For instance, the function could check that the email address is correctly formatted and contains a prefix (the part of the email before the @ symbol) and the domain name after the @ symbol. Our function might check any emails entered against a database of emails commonly used for spam. We could also add a command to prevent duplicate emails by checking that the email data passed to the verifyEmail() function doesn't already exist in our own database. Essentially, we can pack in any set of tasks, then call them as a function.

Where procedural programming comes up short

Procedural programming works great inside simple programs where all the problems can be solved with a dozen or so functions. The functions are neatly nested within each other, they interact with each other, and you can easily transfer data from one function to another.

Let’s imagine you write a function to register a user in an online store. Inside this function, we’ll need to check their email address. This means that we simply call our verifyEmail() function from the previous example inside of our new registerUser() function. Then, depending on the response that verifyEmail() gives back, inside of registerUser(), we can choose to either register the user or throw an error. Keep in mind that this verifyEmail() function is not only mentioned inside of the registerUser() function, but likely in dozens of functions inside that make your online store work properly. These functions will also depend on a true or false result being returned from verifyEmail().

Then let’s imagine that our product manager comes in and says, “If something goes wrong with email verification, I want the user to know exactly what the error was.” Now you need to teach the function to return not only true or false, but also an error code. For example, if there is a typo in the email address, we'll return a code of 01, if the address is used for spam, code 02, and so on. This part isn't that hard to implement, we'll just need to check the different conditions, and implement the code to return these responses as needed.

So, you go inside this function and change its behavior. Now, instead of true or false, it throws an error code, or if there is no error, it returns OK.

And then… your code breaks. This is because a dozen or so functions that expected to get true or false from the email verification function now get OK and fail because of this.

Now we have three options:

  • We could rewrite all the other functions, so that they will understand the email validator’s new response system.
  • We could rewrite the email address validator itself so that it remains compatible with the old parts of the code, but somehow still sends error codes as needed.
  • We could write a new and separate validator function that prints error codes and use it where necessary while still using the old validator in the old parts of your code.

With a basic example like this, we can solve this problem in just a couple of hours.

But imagine that you have hundreds of these functions to change. And since code is constantly changing anyway, you’ll need to make dozens of these changes every day. Further, each change tends to make our functions behave in a more complex manner and produce more complex results. And it seems that every change made in one place breaks three other functions somewhere else. Eventually, you’ll end up with a bunch of cloned functions in a big mess that makes no sense at all.

This is called spaghetti code, and object-oriented programming was invented so that we don’t get dragged down into that mess.

Object-Oriented Programming

The main goal of OOP is to make complex code simpler. To do this, the program is divided into independent blocks that are called objects.

An object isn’t some kind of cosmic entity. It’s just a collection of data and functions similar to the ones used in traditional functional programming. Imagine that you just took a piece of the program, put it in a box, and closed the lid. This closed box is an object.

Earlier programmers agreed that the data inside the object would be referred to as its “properties”, and any functions referred to as the object’s “methods”. This terminology is widely used in discussion and documentation, but it’s important to keep in mind that these are just words. At the end of the day, properties and methods are just data and functionality.

We could also compare an object to a kitchen appliance. Kitchen appliances have a diverse range of functionality. Kettles boils water, stoves heat food, blenders mix everything together, and so on. Inside each individual device, there are a bunch of constituent parts like motors, controllers, buttons, springs, and fuses. However, when we’re actually using one of these devices, we don’t think about these internal things. We just push a button or flip a switch and wait for them to do what we expect them to do. Despite their vast difference in functionality and makeup, they also work together so that at the end of the day — dinner’s ready!

Objects can be characterized by four terms: encapsulation, abstraction, inheritance, and polymorphism.

Encapsulation means that objects are independent. Each object is designed so that all the data it needs is stored inside the object itself, rather than somewhere else in the program. For example, if we have a User object, all the user data will be kept right inside the object, including usernames, user addresses, and anything else related to them. The object will also store all the relevant methods, such as verifyEmail() or subscribeToNewsletter().

Abstraction means that objects have an interface. Objects should have methods and properties that we can access from outside the object. Think about how we press a button on a blender instead of actually opening it up and manually starting it. The blender has a complex design stored inside it (that actually makes it functional), but its control panel only has one button. This button is like an abstract interface in OOP.

In our program, we could call a function to delete a user. Speaking in OOP terms, this would be something like user.delete() — that is, we access our user object and call the delete() method on that user object. It's cool that we don't need to worry about how deletion works behind the scenes because OOP allows us to simply use the desired function.

Let’s consider a real-life example. Two programmers are working on an online store. One is developing an ordering module, and the other one is working on a delivery module. The order module has an order object with a cancel() method. At times, the delivery module needs to cancel orders due to a delivery-related reason. In such cases, the second programmer can simply write order.cancel(). They can use the functionality from the order object inside of the delivery module, and they don't need to worry about how the other programmer actually implemented the cancellation.

Inheritance offers a copying mechanism. With OOP, we can create many objects in the likeness of another object. This saves us from copy-pasting the code and allows us instead to simply reuse it.

For example, we might have a certain User object. We can think of it as being like the ultimate User object. Inside it, we've coded everything that could possibly happen to a user. It could have some properties like name, age, address, and cardNumber; and a few methods such as giveDiscount(), checkOrder(), findOrder(), and call().

Based on this ideal user “template,” we can create a real user. For example, we’ll make the UserTaylor object for a user named "Taylor". Upon creation, this user will have all the properties and methods that we set for the ideal User object, plus — and this is the important part of inheritance — we can also add unique properties and methods to the UserTaylor object, if we need to.

In programming, these “ideal objects” are referred to as classes.

In OOP, polymorphism is made possible by inheritance, and means that objects can share functionality while being adapted as necessary. Continuing from the last example, if we create a User class and give it a call() method for contacting users, this method should act exactly as intended and be written the same everywhere. You shouldn't create a class with a call() method while another has a phone() method. Polymorphism, by way of inheritance, helps us to avoid this.

At the same time, OOP’s principle of polymorphism means we can implement the same method in different ways. For instance, in the event that a user has Skype instead of a phone, the call() method will try and contact them there instead. These method will be different, but this isn't important to the programmer. They'll know to use the call() method on whatever object they are dealing with, be that a VoIP or a Mobile object.

OOP allows you to independently program each bit of reusable JavaScript code (these are called modules). It’s important to think in advance what rules will control the interaction between different modules. With this approach, you can improve how one module works without affecting the rest of them. The program as a whole will not be affected by the contents of the modules if the rules for working with them remain the same.

Pros and cons of OOP

Object-oriented programming has many advantages, and that’s why most programmers use this approach these days.

  1. The code is easier to read. When everything is broken down into objects and they have a clear set of rules, you can quickly understand what each object is responsible for and what it consists of.
  2. The code is less repetitive. For instance, with procedural programming, we might have one function that counts duplicate characters in a one-dimensional array, while another counts duplicates in a two-dimensional array. This means we’ll have redundant code because most of the functionality between the two functions will be the same. In OOP, this is solved by inheritance.
  3. Complex programs are easier to write using the OOP approach. You can divide each large program into several blocks, write the basic logic, and then add more and more details to each block.
  4. It allows you to write code faster and you can quickly create the necessary components within the program in order to make a working prototype.

Of course, like anything, there are also some cons with an OOP approach:

  1. It’s difficult to understand: OOP is much more complex than procedural programming since you need to learn a lot of theory before writing a single line of code.
  2. It requires more resources. The objects in OOP consist of data, interfaces, methods, and other entities. This requires a lot more memory than a simple variable.
  3. Sometimes code performance will be less efficient. Due to the special features of this approach, some things may be more difficult to implement than they would have been using a traditional approach. Simply put, sometimes an OOP program runs slower than a procedural one. However, considering current processor capacities, this is a minor concern.

What lies ahead

Next time, we’ll talk about classes, objects, and everything else associated with OOP. Sounds interesting!

If you want to tackle OOP more in-depth, why not go ahead and change or upgrade your career while you’re at it? At Practicum, we offer online education and mentorship to help you develop a career in tech. You’ll take a deep dive into interesting topics like OOP, web development, data science, and more. Plus, you’ll do it all while working in an awesome and supportive environment with both peers and mentors.

--

--

TripleTen: Part-Time Online Tech Bootcamps
TripleTen Bootcamp

Learn The Job, Get The Job 📈 Online, Part-Time Tech Bootcamps 💡 87% of our grads get hired in 6 months or less 🚀