toscho.design

Hilft objekt-orientertes Programmieren den normalen Nutzern?

In der deutschsprachigen Googe-Plus-Community für WordPress wurde gefragt, ob objekt-orientierte Programmierung in WordPress Vorteile für den normalen Nutzer hätte. Meine Antwort ist ein wenig zu lang für Google ausgefallen, daher reaktiviere ich hiermit dieses Blog.


Die Frage kann man nur beantworten, wenn man vorher benennt, was ein Vorteil für den Enduser ist, was man unter objekt-orientiertem Programmieren versteht und wie beides zusammenwirkt.

Für die normalen Nutzer sind zwei Sachen wichtig:

  1. Sie wollen ihre Aufgabe effizient erledigen. Deshalb ist es auch nicht gut, von Nutzern zu sprechen. Die Leute sind Autoren, Redakteure, Administratoren usw. Diese Rollen wollen sie gut ausführen.
  2. Sie möchten sich auf das System verlassen können. Bei einem Update soll nichts kaputt gehen, bei sehr hoher Last auch nicht, bei vielen Artikeln, Konten … das System muss vorhersagbar funktionieren.

Ein Objekt ist ein Stück Code mit einem eigenen Namen, in dem Zustand und Verhalten kombiniert werden können. Ein Autor hat beispielsweise einen Namen (Zustand) und er kann sich an- und abmelden oder Beiträge veröffentlichen (Verhalten). Typische Objekte sind Algorithmen wie Sortiermechanismen, Typen wie Versionsnummern oder Preise, Controller, die andere Objekte miteinander in Beziehung setzen oder Views, die eine Ausgabe erzeugen.

Im modernen objekt-orientierten Design sind die Abhängigkeiten zwischen Objekten abstrakt: Jedes Objekt kennt nur die eigenen Interna, von anderen Objekten kennt es nur die öffentliche Schnittstelle (das Interface). Dadurch kann man viele Objekte zur Laufzeit austauschen. Wenn ich eine Liste von Beiträgen habe, dann kann ich ihr mal eben so einen neuen Sortieralgorithmus mitgeben, der nicht nach dem ersten Buchstaben sortiert, sondern nach Länge des Inhaltes. Mein neuer Algorithmus muss nur das gleiche Interface aufweisen wie der vorher benutzte, intern funktioniert er ganz anders.

Solche abstrakten Abhängigkeiten gibt es in WordPress fast gar nicht. Das führt oft zu Problemen, wenn zwei Plugins auf denselben Kontext zugreifen wollen: In WordPress schließen sie einander oft aus, aber keiner von beiden kann vorhersagen, wer gewinnt.

Konkretes Beispiel: Ich möchte in Multilingual Press gerne Menüpunkte für Sprachen einbauen. In der Administration soll man dann für jede Sprache einstellen können, wie sie präsentiert wird. Allerdings verwendet WordPress hier prozeduralen Code, also einen, der sich auf einen statisch vorgegebenen Ablauf bezieht. Man kann nur alle Menüpunkte ändern oder keinen. Wenn zwei Plugins das versuchen, wird eines verlieren.

sprachmenu

Der Nutzer weiß das nicht. Ihm fehlt nur ein Feature, bei dem der Support beider Plugins mit den Schultern zuckt und sagt: geht halt nicht.

Objekte mit abstrakten Abhängigkeiten kann man separat automatisiert testen. Immer wenn ich eine Änderung an meinem Code vornehme, lasse ich ein Werkzeug automatisch alle existierenden Tests durchlaufen, die prüfen, ob ich damit etwas kaputtmache, das vorher funktioniert hat. Bei prozeduralem Code ist das viel schwieriger, weil durch die feste Abhängigkeit keine einzelnen Teile (Units) getestet werden können, sondern immer nur sehr große Codeblöcke. Dadurch wird es schwerer, Fehlerquellen zu finden und neue Fehler zu vermeiden.

Es gibt in WordPress nur wenige echte Units, die man testen kann. Prozeduraler Code verleitet stark zu enormer Schlampigkeit. Ein Beispiel ist die Methode WP_Query::get_posts(). Die holt Beiträge aus der Datenbank, sie ist also wirklich essentiell. Durch diese Methode gibt es 1.435.733.941.397.422.709.124.940.625.188.500.371.668.992.000.000 mögliche Wege. So viele Unittests bräuchte man, um sie vollständig zu testen. Anders ausgedrückt: Wenn sich in diese Methode ein Fehler einschleicht, wird man ihn wahrscheinlich nicht finden, ehe die Nutzer (Autoren, Redakteure …) damit arbeiten müssen.

Eine Besonderheit in PHP ist es, dass man Klassen, die Vorlagen für Objekte, automatisch laden lassen kann. Das nennt man Autoloading. Das heißt, dass man eine Datei mit Code erst dann einbindet, wenn man den Code tatsächlich benutzt. In WordPress gibt es so etwas nicht, da wird immer alles auf Vorrat geladen, für den Fall, dass es eventuell gebraucht werden könnte. Zwar sind Klassen nicht identisch mit OOP, striktes OOP harmoniert aber gut mit Autoloading, dadurch wäre es schneller als der derzeitige Code.

Durch eine gute Abstraktion wäre auch viel Code wiederverwendbar. Man müsste also beispielsweise im Backend nicht immer wieder die gleichen Formularelemente (Eingabefelder) im Code haben, sondern nur eine Klasse, die jeweils ein solches Element erzeugt und die dann mit unterschiedlichen Parametern bestückt wiederverwendet werden kann.

Guter OOP-Code ist einfach. Er ist leider auch selten, deshalb halten viele Entwickler OOP für komplex.

Guter OOP-Code lässt sich schnell und gründlich testen. Er geht bei einem Update nicht so schnell kaputt – wenn man testet – und der Nutzer kann sich besser auf ihn verlassen.

Guter OOP-Code erleichtert globale Änderungen. Man muss ein Problem nur noch einmal reparieren und alle betroffenen Stellen funktionieren dann besser und konsistent. Das erleichtert dem Nutzer das effiziente Arbeiten, zumindest wenn diese Änderungen nützlich sind.

Insgesamt täte ein bißchen OOP WordPress sicher gut. Allerdings habe ich hier wenig Hoffnung. Selbst neuer Code wird zwar oft in Klassen gepackt, dann aber doch rein prozedural geschrieben. Als Entwickler widert mich der Code oft an, er versaut mir die Freude an der Arbeit, weil er oft auf sehr komplexe Art dumm ist.

2 Kommentare

  1. edik am 16.09.2014 · 20:18

    Ich bin nicht gerade ein OOP-Meister. Aber wo erleichtert OOP-Code denn globale Aenderungen? Ich sehe da zumindest keine triviale Begruendung. Kann man bei der prozeduralen Programmierung nicht genauso gut ein Problem an nur einer einzigen Stelle beheben, vorrausgesetzt, der Code enthaelt keine Redundanzen?

  2. Thomas Scholz am 22.09.2014 · 00:05

    @edik: Im normalen OOP werden Abhängigkeiten zwischen Objekten abstrahiert, die Objekte hängen also von Interfaces ab, nicht von konkreten Implementationen. So kann man die realen Implementationen leicht austauschen, ohne den empfangenden Code zu ändern.

    Ein Beispiel: Ich habe eine Navigation, die Links zu anderen Sprachversionen erzeugt. Jedes Element hat einen Abhängigkeit von einem Language_Interface mit ein paar öffentlichen Methoden: get_priority() (für Content-Negotiation), is_rtl(), get_name( $type ) (nativer Name, englischer, vom Benutzer festgelegter, whatever).

    Irgendwann möchte ich die Interna der konkreten Implementation ändern, beispielsweise die Priorität anders prüfen oder einen Filter auf den Namen einbauen, damit man den von außen ändern kann.

    Dazu muss ich die Navigation nicht anfassen, auch die Content-Negotiation nicht oder die Tabelle, die alle Sprachen zum Verwalten anzeigt. Nur die eine konkrete Implementation, und selbst die kann ich einfach so lassen und nur in einigen Fällen meine zweite Version übergeben. Ich kann also aus einer Implementation mehrere machen oder umgekehrt mehrere zusammenführen, ohne neue öffentliche Parameter einzuführen oder bestehenden abhängigen Code zu ändern.

    Ich kann auch einen Decorator zwischen die ursprüngliche Implementation und den Empfänger legen. Beispiel eines sehr einfachen URL-Decorators.

    Das ist sehr einfach umzusetzen und auch zu testen. Geht mit prozeduralem Code kaum oder gar nicht.