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.
What you will learn
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.
// 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.');
}
}
}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.
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.');
}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
Further sources
- PHP manual: Classes and objects Encapsulation, inheritance, interfaces
- PHP manual: Object interfaces the language side of interfaces
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.