I would like to begin from basics and remember us what object-oriented programming (OOP) is and why it is super-useful if you are doing it right. Base concept of OOP has few terms you should aware of: class and object. So what is the difference between them?

Class and objects in the OOP concept

As you can see from the image above, the class is an a template, that describes a plan how objects could be created from it and how they could be re-used several times in several contextes. Everything around us are objects that brlongs to some classes and could be easialy described using classes. Some of objects around you are simple and could be described using simple classes. For example, let's take a pen on your desk: it has some design, shape and color; it can be automated, or with a cap; inside there is a rod of different colors and different levels of occupancy. Using classes and objects in programming is endowing the code with more natural logic for understanding by humans, since OOP describes the real world around us.

In addition to the fact that each class describes a logical concept, the class also contains some context or state, and may have some kind of functionality. Data is stored in the class properties. If we take a Pen, then its properties that are of interest to us may be material, color, shape; if we want to describe the House using classes, then the properties will be the number of windows, doors, floors, etc.

The functionality of classes is described using methods, that is, functions that our class can perform. If we want descriptive functionality of the Car class, then the main methods will be open cars, turning on the engine, driving, stopping, turning off the engine, and closing.

Sounds easy, isn't it? 🙂

class Car
{
    private int $wheelCount;
    private int $doorsCount;
    private bool $isOpen = false;
    private bool $isStarted = false;

    public function __construct(int $wheelCount, int $doorsCount)
    {
        $this->wheelCount = $wheelCount;
        $this->doorsCount = $doorsCount;
    }

    public function open(): void
    {
        echo "Opening all {$this->doorsCount} doors of car.";

        $this->isOpen = true;
    }

    public function start(): void
    {
        if ($this->isOpen) {
            echo 'Staring an engine for car.';

            $this->isStarted = true;
        }
    }

    public function stop(): void
    {
        echo "Stopping car and blocking all {$this->wheelCount} wheels.";

        $this->isStarted = false;
    }
}

$sedan = new Car(4, 2);
$sedan->open();

$truck = new Car(6, 4);
$truck->open();
$truck->start();

We just created two objects $sedan and $truck of Car class. They both are instances of the same class, but have different properties. The default values were initialized using the constructor method, which is called automatically. The $sedan has 4 wheels and 2 doors in our application, and the $truck has 6 wheels and 4 doors. Both of these machines have common functionality, as described in the template of their behavior, that is, the class. At the moment, the objects have a different state, one of them is just open, and the second has already begun to move.

OOP paradigms kiss

Encapsulation

The implementation and state of each object are privately held inside a defined boundary, or class. Other objects do not have access to this class or the authority to make changes but are only able to call a list of public functions, or methods. This characteristic of data hiding provides greater program security and avoids unintended data corruption.

In other words, encapsulation is the concealment of some elements of the system in order to prevent other sources from modifying them.

Let's go back to objects from real life:

  • When you enter the room and turn on the light, you only need to turn on one switch. You do not need to know the information about the wires and how they are connected, if you are not an electrician.

  • When you get into your car, you also have limited functionality with the interface available to you: the ignition key, steering wheel and pedals. Everything else is hidden from you for your own safety. Agree, it would be very inconvenient if instead of a comfortable chair you would have to sit among a pile of cables, pistons, and manually manage all this.

  • When you work with your computer, you are only interested in what you see on the monitor, what you write using the keyboard, or how you control the mouse. For the average user, the rest of the functionality is hidden. If you call the repair wizard, he can open the system unit, and another level of access will be available to him. Nevertheless, he still does not have to think about how microelements interact with each other, such details can only be considered at the factory.

Encapsulation implementation in different programming languages is represented using access modifiers: public, protected, private.

Public, protected and private access modifiers

  • public accessible for everyone from everywhere;
  • protected accessible for every child classes;
  • private accessible only inside the class.

Inheritance

Inheritance is one of the OOP properties that makes it possible to inherit a parent class and use its properties and methods, depending on access modifiers. It forces a more thorough data analysis, reduces development time and ensures a higher level of accuracy.

Inherited car types

In the picture above you can see various types of cars, but nevertheless they are all inherited from the parent class Class. Moreover, inheritance is not limited to one level and we can create another layer of inheritance, for example SUV mini, SUV compact, SUV midsize, SUV fullsize...

Car subtypes

Inheritance is contrasted with object composition, where one object contains another object (or objects of one class contain objects of another class).

Inheritance tree of musical instruments

Composition over inheritance

One of main difference between Composition and Inheritance comes from Encapsulation and robustness point of view. Though both Inheritance and Composition allows code reuse, Inheritance breaks encapsulation because in case of Inheritance, sub class is dependent upon super class behavior. If parent classes changes its behavior than child class is also get affected. If classes are not properly documented and child class has not used the super class in a way it should be used, any change in super class can break functionality in sub class.

Classes and objects created through inheritance are tightly coupled because changing the parent or superclass in an inheritance relationship risks breaking your code. Classes and objects created through composition are loosely coupled, meaning that you can more easily change the component parts without breaking your code.

Because loosely coupled code offers more flexibility, many developers have learned that composition is a better technique than inheritance, but the truth is more complex. Choosing a programming tool is similar to choosing the correct kitchen tool: You wouldn't use a butter knife to cut vegetables, and in the same way you shouldn't choose composition for every programming scenario.

Composition over inheritance

Use inheritance when we know there is an "is a" relationship between a child and its parent class:

  • A person is a human.
  • A cat is an animal.
  • A car is a vehicle.
abstract class Doctor
{
    protected \DateTimeImmutable $graduatedAt;
    protected array $skills = [];

    abstract public function cure(): void;
}

class Dentist extends Doctor
{
    public function cure(): void
    {
        // Treat teeth
    }
}

class Surgeon extends Doctor
{
    public function cure(): void
    {
        // Perform operations
    }
}

Use composition in cases where one object "has a" (or is part of) another object. Some examples would be:

  • A car has a battery (a battery is part of a car).
  • A person has a heart (a heart is part of a person).
  • A house has a living room (a living room is part of a house).
class Child
{
    private Mother $mother;
    private Father $father;

    public function __construct(Mother $mother, Father $father)
    {
        $this->mother = $mother;
        $this->father = $father;
    }

    public function lastName(): string
    {
        return $this->father->lastName();
    }

    public function health(): int
    {
        $combinedHealth = $this->mother->health() + $this->father->health();

        return $combinedHealth / 2;
    }
}

Unfortunately, many who do not know such a feature as composition blame OOP for insufficient flexibility. But now we know how to make our application more modular, better testable and less coupled. 😉

Polymorphism

Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object. Objects are allowed to take on more than one form depending on the context. The program will determine which meaning or usage is necessary for each execution of that object, cutting down on the need to duplicate code.

Animals talk about polymorphism

Classes that are inherited from the parent, or composed from other classes, can have their own implementation.


Abstraction

Objects only reveal internal mechanisms that are relevant for the use of other objects, hiding any unnecessary implementation code. This concept helps developers make changes and additions over time more easily.

Car abstraction

Generally speaking, everything that we see around us, we perceive abstractly. We all see the car in the picture above, and we just had to see a few abstract details. You can determine whether it is a car, a bicycle or a bus simply by paying attention to a few details. And this applies to everything around us.

When you want to write a message to a friend, you take a smartphone in your hand, and you absolutely do not care at that moment what form it is, the manufacturer, what its battery capacity or screen brightness is. In any case, the main abstraction will not change, it will still be a smartphone.

Criticism of OOP

The idea of object-oriented programming has been criticized by developers for multiple reasons. The largest concern is that OOP overemphasizes the data component of software development and does not focus enough on computation or algorithms. Additionally, OOP code may be more complicated to write and take longer to compile. Alternative methods to OOP include functional programming, structured programming and imperative programming, but most advanced programming languages give developers the option to combine them.


TL;DR: Conclusion

The important things to understand at this early stage are the principles the OOP paradigm is based on and the benefits they provide:

  • Objects modeled on real-world things are the centerpiece of any OOP-based application.
  • Encapsulation protects data from uncontrolled access.
  • Objects have functions that operate on the data the objects contain.
  • Classes are the templates used to instantiate objects.
  • Inheritance is a powerful tool for avoiding redundancy.
  • OOP is more verbose but easier to read than other coding paradigms.