Skip to content

What is object-oriented programming?

The usual explanations rely on dogs, cats and vehicles. This article explains the four central ideas using a task of the kind that actually occurs in business applications — and says where the idea reaches its limits.

  • Who it is forBeginners, technically curious readers
  • LevelFundamentals
  • Reading timeabout 8 minutes
  • Last updated16 October 2026

What you will learn

  • What an object is and how it differs from an array
  • What encapsulation means in practice
  • What interfaces are for — the most important part
  • Why inheritance is less often the answer than it seems

An object is a thing with rules

An array can contain anything. An object contains exactly what its class provides for, and it makes sure itself that it stays in a valid state. That is the whole difference — and it is a big one.

PHPtested with PHP 8.3
// As an array: nothing stops you storing nonsense
$urlaub = ['von' => '2027-08-01', 'bis' => '2027-07-01'];
// End before start — only noticed somewhere further down the line

// As an object: the case cannot arise at all
final class Urlaubszeitraum
{
    public function __construct(
        public readonly DateTimeImmutable $von,
        public readonly DateTimeImmutable $bis,
    ) {
        if ($bis < $von) {
            throw new InvalidArgumentException('Ende liegt vor dem Beginn.');
        }
    }
}
The decisive statement is in the constructor: a period with swapped dates does not exist in this application.

Encapsulation: only what is needed on the outside

Encapsulation means that an object’s data cannot be changed from outside, only through methods that know the rules. In practice this means: when a rule changes, you change it in one place instead of hunting through all the callers.

The counter-test is simple: can an object be brought from outside into a state that the business rules say must not exist? Then encapsulation is missing.

Interfaces: the most important part

An interface — in the sense of the language, not of data transfer — defines what an object must be able to do, without saying how. That sounds abstract and is the most practical part of the whole idea.

PHPtested with PHP 8.3
interface Benachrichtigung
{
    public function sende(string $empfaenger, string $text): bool;
}

final class EMailBenachrichtigung implements Benachrichtigung
{
    public function sende(string $empfaenger, string $text): bool
    {
        // ... actual sending
        return true;
    }
}

// The caller only knows the interface:
function informiere(Benachrichtigung $weg, string $an): void
{
    $weg->sende($an, 'Ihr Auftrag wurde angenommen.');
}
A second channel — for example a message in the portal — is added without informiere() changing.

That is exactly what interfaces are for: they separate the decision “what should happen” from the decision “by what means”. In a test, the same place can be filled with a simple stand-in that sends nothing but records what it received.

A real-world example

Why inheritance fits less often than expected

Inheritance promises to write what is shared once. In practice it often produces a chain in which a change at the very top takes effect in three unexpected places further down. The question that helps: is the subclass really a special form — or does it merely use something?

A real-world example: a “rush order” is not a special form of an “order” with different behaviour, but an order with a different delivery method. Built with inheritance, the second special form already needs multiple inheritance, which does not exist. Built as a property, it stays one line.

The common rule of thumb is therefore: compose first, inherit only then — and inherit mainly where a shared interface makes sense.

Common misconceptions

No. For a stateless conversion, a function is the clearer solution. Objects pay off where there are rules about data.

That is a widespread pattern, but not object orientation: if all the classes merely pass values along, the business logic is still scattered.

Until the day a second channel is added or something has to be testable. Then they are the difference between one line and a rebuild.

Further sources

About the author

Fabian Lücker is managing partner of PHP-Schmiede KG in Rheine and has worked in software development since 2007. He is responsible for development and design, and writes here about decisions that come up in real projects.