OOP Key Principles
Zoltán Szilágyi
2025-03-01
All publications

The key concepts in Object-Oriented Programming

Object-oriented programming is based on four pillars, concepts that differentiate it from other programmingparadigms.

Abstraction

Most of the time when you’re creating a program with OOP, you shape of the program based on real-world objects. However, objects of the program don’t represent the originals with 100% accuracy (and it’s rarely required that they do). Instead, your objects only model attributes and behaviors of real objects in a specific context, ignoring the rest.

For example, an Airplane class could probably exist in both a flight simulator and a flight booking application. But in the former case, it would hold details related to the actual flight, whereas in the latter class you would care only about the seat map and which seats are available.

Abstraction

Abstraction is a model of a real-world object or phenomenon, limited to a specific context, which represents all details relevant to this context with high accuracy and omits all the rest.

Encapsulation

To start a car engine, you only need to turn a key or press a button. You don’t need to connect wires under the hood, rotate the crankshaft and cylinders, and initiate the power cycle of the engine. These details are hidden under the hood of the car. You have only a simple interface: a start switch, a steering wheel and some pedals. This illustrates how each object has an interface — a public part of an object, open to interactions with other objects.

Encapsulation is the ability of an object to hide parts of its state and behaviors from other objects, exposing only a limited interface to the rest of the program.

To encapsulate something means to make it private, and thus accessible only from within the methods of its own class. There’s a little bit less restrictive mode called protected that makes a member of a class available to subclasses as well.

Interfaces and abstract classes / methods of most programming languages are based on the concepts of abstraction and encapsulation. In modern object-oriented programming languages, the interface mechanism (usually declared with the interface or protocol keyword) lets you define contracts of interaction between objects. That’s one of the reasons why the interfaces only care about behaviors of objects, and why you can’t declare a field in an interface.

The fact that the word interface stands for a public part of an object, while there’s also the interface type in most programming languages, is very confusing. I’m with you on that.

Imagine that you have a FlyingTransport interface with a method fly(origin, destination, passengers). When designing an air transportation simulator, you could restrict the Airport class to work only with objects that implement the FlyingTransport interface. After this, you can be sure that any object passed to an airport object, whether it’s an Airplane, a Helicopter or a freaking Domesticated Gryphon would be able to arrive or depart from this type of airport.

Encapsulation

You could change the implementation of the fly method in these classes in any way you want. As long as the signature of the method remains the same as declared in the interface, all instances of the Airport class can work with your flying objects just fine.

Inheritance

Inheritance is the ability to build new classes on top of existing ones. The main benefit of inheritance is code reuse. If you want to create a class that’s slightly different from an existing one, there’s no need to duplicate code. Instead, you extend the existing class and put the extra functionality into a resulting subclass, which inherits fields and methods of the superclass.

The consequence of using inheritance is that subclasses have the same interface as their parent class. You can’t hide a method in a subclass if it was declared in the superclass. You must also implement all abstract methods, even if they don’t make sense for your subclass.

Inheritance

In most programming languages a subclass can extend only one superclass. On the other hand, any class can implement several interfaces at the same time. But, as I mentioned before, if a superclass implements an interface, all of its subclasses must also implement it.

Polymorphism

Let’s look at some animal examples. Most Animals can make sounds. We can anticipate that all subclasses will need to override the base makeSound method so each subclass can emit the correct sound; therefore we can declare it abstract right away. This lets us omit any default implementation of the method in the superclass, but force all subclasses to come up with their own.

Polymorphism

Imagine that we’ve put several cats and dogs into a large bag. Then, with closed eyes, we take the animals one-by-one out of the bag. After taking an animal from the bag, we don’t know for sure what it is. However, if we cuddle it hard enough, the animal will emit a specific sound of joy, depending on its concrete class.

bag = [new Cat(), new Dog()];
foreach (Animal a : bag) {
    a.makeSound()
}
 
// Meow!
// Woof!

The program doesn’t know the concrete type of the object contained inside the a variable; but, thanks to the special mechanism called polymorphism, the program can trace down the subclass of the object whose method is being executed and run the appropriate behavior. Polymorphism is the ability of a program to detect the real class of an object and call its implementation even when its real type is unknown in the current context. You can also think of polymorphism as the ability of an object to “pretend” to be something else, usually a class it extends or an interface it implements. In our example, the dogs and cats in the bag were pretending to be generic animals.