Objekte

Der Begriff Objektorientierung wurden von Alan Kay gegen Ende der 1960-er Jahre geprägt. Es schrieb selbst dazu:

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning -- it took a while to see how to do messaging in a programming language efficiently enough to be useful).

Alan Kay

Objekte sind also nicht nur ein Add-On zu prozeduraler Programmierung, sondern kommunizieren miteinander mittels Nachrichten. Kein Wunder, dass Objektorientierung und eventgetriebene Architekturen so gut zusammenpassen - sie sind sehr eng miteinander verwandt.

Klassen und Objekte

Eine Klasse ist sozusagen der Bauplan für ein Objekt:

<?php declare(strict_types=1);

class Counter
{
    private int $counter = 0;

    public function increment(): void
    {
        $this->counter += 1;
    }
}
class.php

Hier haben wir ein Objekt, das einen Zähler repräsentiert. Ein Objekt kapselt Daten (den Zählerwert, anfangs 0) sowie Code beziehungsweise die Algorithmen, die auf diesen Daten operieren. Diese Idee steht in relativ krassem Gegensatz zu der Tatsache, dass Quellcode normalerweise in einer Versionskontrolle verwaltet wird, die Daten einer Anwendung aber in einer Datenbank oder ähnlichen Persistenz-Mechanismen, also getrennt voneinander abgelegt werden.

Den Code nennen wir Methoden, auch wenn dort in PHP function steht. Wir können uns eine Methode als eine Funktion vorstellen, die innerhalb eines Objekts existiert.

Weiterhin können Objekte Daten enthalten, in unserem Beispiel den Zählerwert, der ein int ist. Diese Daten nennen wir Attribute oder Properties des Objekts. Um innerhalb eines Objektes auf seinen Zustand, also die aktuellen Attributwerte zuzugreifen, verwenden wir die spezielle Variable $this. Für den Aufruf einer Methode verwenden wir den Pfeil ->, der in PHP auch Object Operator genannt wird. In anderen Programmiersprachen wird anstelle des Pfeils . verwendet.

$this

$this ist ein reservierter Variablenname. Wenn Du versuchst, die Variable $this neu zuzuweisen, führt das direkt zum Abbruch des Programms:

<?php declare(strict_types=1);

class Counter
{
    public function increment(): void
    {
        $this = new Counter;
    }
}
reassignThis-will-fail.php
PHP Fatal error:  Cannot re-assign $this in reassignThis-will-fail.php on line 7
Ausgabe von reassignThis-will-fail.php
reassignThis-will-fail.php ausführen

Wie wir in diesem Beispiel gerade schon gesehen haben, verwenden wir new, um aus einer Klasse ein Objekt zu erzeugen. Das gibt uns eine Referenz auf die erzeugte Objektinstanz zurück, über die wir dem Objekt Nachrichten senden können:

<?php declare(strict_types=1);

class Counter
{
    private int $counter = 0;

    public function increment(): void
    {
        $this->counter += 1;
    }
}

$counter = new Counter;
$counter->increment();

var_dump($counter);
instance.php
instance.php:16:
class Counter#1 (1) {
  private int $counter =>
  int(1)
}
Ausgabe von instance.php
instance.php ausführen

Anstelle von "Nachricht senden" würden viele Menschen sagen, dass wir eine Methode aufrufen. Das klingt eher danach, eine Funktion aufzurufen. Hier sehen wir wieder das Spannungsfeld zwischen der Idee von Messaging ("OOP wie es eigentlich gedacht war") und einem eher klassischen Programmierverständnis (Funktionsaufrufe), das in der prozeduralen Programmierung wurzelt.

Wir können mehrere Zähler erzeugen, die voneinander unabhängig sind. Insbesondere hat jedes Zähler-Objekt seinen eigenen Zustand:

<?php declare(strict_types=1);

class Counter
{
    private int $counter = 0;

    public function increment(): void
    {
        $this->counter += 1;
    }
}

$counter1 = new Counter;
$counter1->increment();
$counter1->increment();

$counter2 = new Counter;
$counter2->increment();

var_dump($counter1, $counter2);
counters.php
counters.php:20:
class Counter#1 (1) {
  private int $counter =>
  int(2)
}
counters.php:20:
class Counter#2 (1) {
  private int $counter =>
  int(1)
}
Ausgabe von counters.php
counters.php ausführen

var_dump() zeigt uns dabei für jedes Objekt hinter dem Klassennamen (das #1 beziehungsweise #2) an, das wievielte in diesem Programmdurchlauf erzeugte Objekt ausgegeben wir da sehen. Zusätzlich wird uns der Zustand des Objektes in Form der aktuellen Attributwerte ausgegeben.

var_dump() ist ein guter Debugger, der immer vorhanden ist.

Weiter geht's

Die Sichtbarkeit, also das private und das public in den obigen Beispielen, ist eine eigene Sache für sich, die mehr Erläuterung braucht.

Um ein Objekt in einen arbeitsfähigen Zustand zu versetzen, brauchen wir einen Konstruktor.

Wenn wir nun fleißig Klassen definieren und dann auch noch Third-Party-Code in unsere Anwendung bringen, entstehen mit großer Wahrscheinlichkeit Namenskonflikte, gerade für so einfache und häufig verwendete Klassennamen wie etwa Node, String, oder Item. Um solche Namenskonflikte zu vermeiden, gibt es in PHP Namensräume (Namespaces).

Fragen zur Lernkontrolle

  1. Erläutere den Unterschied zwischen einer Klasse und einem Objekt.
  2. Welche Vorteile haben Objekte gegenüber prozeduraler Programmierung?
  3. Was macht die Garbage Collection?