The Quick and Dirty Guide to Object-Oriented Principles

Object-oriented programming is by no means new, but it’s definitely not old news either. Even though functional programming and similar may be the new hotness, Object-Oriented programming (OOP) is still arguably the backbone for the majority of the industry. OOP is the natural evolution of procedural programming and turns lone functions and data structures into reusable, encapsulated objects and allows for greater abstraction.

OOP isn’t infallible though, it has its limits and its weaknesses. Functional programming, or any programming paradigm for that matter, is the same. Different approaches provide different advantages and different disadvantages. No one approach is perfect. We’re going to cover the strengths and the weaknesses of OOP.

The Basics of Object-Oriented Programming (OOP)

The primary concepts which make up OOP are: Encapsulation, Polymorphism, and Inheritance. These concepts allow for the creation of Classes and Objects which lead to a greater degree of Abstraction. We’re going to go over the concepts first, then what they mean, as well as their advantages and limitations.

Encapsulation

Image by Hannah Alkadi from Pixabay

Encapsulation is the combination of data and operations to create a single structure which can contain both. You end up with variables and functions or methods embedded in the same structure. Encapsulating both data and operations on said data in a single structure just makes sense in a lot of ways. This allows code to be neater since you can package everything up into an object and allows for more abstraction.

Polymorphism

Polymorphism is the ability for functions to take multiple forms. Basically, one function can take different types of variables and have an expected behavior. So an “add” function may add integers or concatenate strings as necessary. One function can be further abstracted to do one type of operation with different data in a way that makes conceptual sense.

Inheritance

Inheritance is the concept of an object inheriting properties from another object. This allows something like an Animal class to be defined, then for a Dog class to build off of this. Inheritance allows an overriding of functions in derived classes as well.

Classes and Objects

Image by Gianni Crestani from Pixabay

Classes and Objects are two sides of the same coin. Classes are the definition, and Objects are the actual implementation. You have an Animal Class which defines what an Animal is, and your animal1 Object which is what you work with in your program. Having an Object means that the individual implementation of the Class is self-contained and abstracted from the Class. A lot of people will use these words interchangeably though.

Abstraction

Abstraction is pretty self-explanatory. The goal of OOP is to abstract our code so that we can focus on higher level concepts instead of being bogged down by the implementation of basic ideas. The Object is the implementation and the Class is the abstraction. Encapsulation allows us to combine conceptual units into one Object which is an abstraction of data and methods. All of these concepts ultimately lead to further abstraction over procedural programming.

What Is OOP Good For?

OOP extends on procedural programming and is its natural evolution. Procedural programming added the ability to have arbitrary methods or functions available to process data at a later stage in the program which were also reusable. OOP allows the data be packaged with the methods which act on them.

Going Deeper With Data

Data can also typically be protected as private (only the object itself can access) or protected (only the object and trusted objects or classes can access), or public and fair game in most OOP languages. This makes it easy to create programs where foreign code cannot tamper with internal variables. It may not sound like a big deal, but this means a given object can be written so that arbitrary changes to other code will not cause unexpected behavior. Think of it as the difference between having a safety or not on a gun. It’s near impossible to misfire without either a defect or an intentional action.

Reduce and Reuse

Image by BRRT from Pixabay

OOP also allows for reuse of code. The structures like structs and general functions or methods which procedural languages add make for great reusability in code, but objects and classes just up the ante. You don’t need to remember to grab that random function which operates on your data structure, you just grab the class. The goal of a given data structure and its methods are contained and abstracted into another structure which can be reused.

Adding in principles like inheritance and polymorphism means that instead of having to take the raw data, a given class or object can work with other forms of objects. As the objects get more and more abstracted, they get more and more dynamic. A number could be an int, a float, a double, or any other number type a language supports.

This leads to dynamically typed languages where a scalar is a scalar (single variable or unit of data). Basically, context dictates what type a variable is, and context can change. Statically typed languages require that the data be typed and will error out without either casting the data, or having some other kind of conversion. Both have their use cases, but dynamically typed rely heavily on OOP concepts. There is (a lot) more to this, but it is outside the scope of what I could hope to fit into this article.

Types of OOP

There are countless languages with OOP concepts baked in. Almost any language newer than the 80s or so will have OOP of some kind designed in. Even the really old stuff has had OOP concepts bolted on.

The big difference is how committed the language is to each concept. Some languages like Java or C# require OOP and all of its principles be employed in even the most trivial programs, others like Lua or Perl allow OOP, but don’t require it either. Languages like Python are built around OOP concepts where every basic data type is an object but don’t necessarily require OOP habits. The different philosophies lead to different functionality and use cases.

What Is OOP Bad At?

OOP as it stands is neither good nor bad, but the implementation really shapes the usefulness. All of the concepts of OOP were promised as being infinitely reusable and allowing massive chains of inheritance to where a dog class inherited from a mammal class which inherited from a vertebrate class which inherited from an animal class which inherited from a life class and so on and so forth. In practice, no one does this unless they don’t know better.

Abstraction Vs. Reality

Image by 8926 from Pixabay

Too much abstraction leads to a complete disconnect from reality. As classes give way to templates, abstract classes, and all sorts of other abstract ideas, the code gets harder and harder to maintain. Green Java programmers are notorious for writing a program which is 10 lines in pretty much any other language with hundreds in Java. “Hello World” doesn’t need polymorphism or inheritance. It doesn’t need templates or verbose error handling.

Excess abstraction just leads to unmaintainable code. You don’t need to keep track of the number of hairs on each sheep if you’re just trying to count them. Minor changes lead to confusion down the line. A change to an animal class may lead to unintended behavior in a derived class down the chain. This disconnect leads to having no idea where the piece which needs to change is, or confusion on where whatever was changed might be.

Taking the Good With the Bad

Image by PublicDomainPictures from Pixabay

OOP offers a toolkit which can be very powerful, but can also be a double-edged sword. By knowing when to employ OOP concepts and, even more importantly, when not to, you can save a massive amount of time and wasted effort. Excess abstraction leads to an over-engineered, labyrinthine mess. Too little leads to an untamed jungle of code.

Java has become near-reviled in some circles due to the way it enables terrible coding practices. The language itself isn’t bad, but the habits it encourages, directly and indirectly, have made it the redheaded, bastard step-child of languages in most shops. Java abstracted itself to the point of absurdity and has since served as a warning to other languages.

Conservative usage of inheritance, sane encapsulation, and reasonable polymorphism leads to maintainable, scalable abstraction. Conservative inheritance means fewer classes to keep track of for a single object and fewer places an object can have unintended behavior introduced. Sane encapsulation means smaller, more discrete units which are more conceptually sound. Reasonable polymorphism means more flexibility without trying to include handling for impossible conditions or insane scoping.

Conclusion

OOP is necessary for modern coding. Even malware is using the concepts to get more advanced. It can be both good and bad, but you decide to push it one way or another. Use discretion with how abstract you take the core concepts of inheritance, encapsulation, and polymorphism so that you keep your feet on the ground. OOP is powerful and is the basis of many modern paradigms, but it can also be easily abused.

This is part of my Lua tutorial series.

Featured image by Mabel Amber, still incognito… from Pixabay

Some Dude:

View Comments (2)

  • OOP is not only Inheritance, Polymorphism and Encapsulation, and in some definitions does not apply to these concepts at all.
    My opinion is that these concepts are useless for beginners, especially without clear description of the goal we want to achieve, and without cohesion/coupling concepts.
    The interviewers asking newbies for such things are bad and in 99% cases the are don't clearly understand what is OOP themselves.

    • While I agree with some of your sentiment, I feel it is putting the cart before the horse. I agree that the concepts are useless without reinforcement, but the principles themselves need to be understood or else you are effectively trying to do science by alchemy in the age of quantum physics. This specific article is for the concepts, the next one planned in my series is for the actual practice. I always learned and taught by following the principles first, their application, then further refinement. You show the "rules", then how to employ the "rules", and finally how and when to break the "rules" or when the "rules" are not really rules at all. OOP as defined does not exist without these principles. These principles can be ignored for certain paradigms, but "pure" OOP is explicitly defined by these principles. I appreciate your input!