Programm to an Interface, Dude!

Heute mag ich mal ein einfaches Thema aufgreifen, welches jedoch noch nicht wirklich seinen Weg in die PHP-Welt gefunden hat. Es geht um Interfaces. Seit Version 5 kann man in PHP viele der lange vermissten OOP-Features nutzen, aber leider machen viele Entwickler dennoch einen großen Bogen darum.

Als ich mich vor ein paar Jahren auf meine Java-Zertifizierung vorbereitet habe, ist mir ein Satz immer wieder begegnet: “Programm to an interface, not to an implementation!”. Klingt einfach, ist es auch.

Im Prinzip sagt das nur aus, dass man sich beim Entwickeln nur darauf verlassen sollte, was die jeweilig implementierten Interfaces an Funktionalität anbieten. Wie das ganze dann im Hintergrund implementiert ist, interessiert dann nicht mehr.

Ein kleines Beispiel aus der Praxis: In einem Shop (in dem Fall Magento) werden Bestellungen (Orders) gespeichert. Nun soll ein ERP-System angebunden werden. Für beide Systeme gibt es entsprechende Models, mit denen die Daten verarbeitet werden können.

Soweit, so gut. Jetzt kommt der unbequeme Teil der Aufgabe. Das Magento-Order-Model erbt einfach nur von verschiedenen anderen Klassen, aber es existiert kein Interface, das dem Entwickler bestimmte Methoden garantiert. An vielen Stellen werden zudem die Parameter mit Type-Hinting überprüft, ob sie eine Magento-Order enthalten.

Das Model für die Anbindung an das ERP-System hat unter Umständen andere Elternklassen und somit auch erstmal keine Gemeinsamkeiten. Das bedeutet, dass wir theoretisch die Objekte gleich behandeln könnten, aber wir sie für das Type-Hinting doch wieder in Wrapper verpacken oder auf Magento-Order-Objekte mappen müssen.

Diese Arbeit könnte man sich komplett sparen, wenn man sich von Anfang an auf ein Interface verlassen hätte. Leider existiert dieses meines Wissens nicht und es für eigene Projekte zu nutzen würde Änderungen in den Magento-Core-Dateien fordern.

<?php
interface Mage_Sales_Model_Order_Interface
{
  /**
   * Retrieve customer model
   *
   * @return Mage_Customer_Model_Customer_Interface
   */
  public function getCustomer();

  // ...
}

Damit könnte man sich auf verschiedene Methoden verlassen die der Typ Mage_Sales_Model_Order_Interface (!) zusagt.

<?php
class Mage_Sales_Model_Order
  extends Mage_Sales_Model_Abstract
  implements Mage_Sales_Model_Order_Interface
{
  // ...
}

Die Klasse Mage_Sales_Model_Order könnte sich an diesen “Vertrag” halten,

<?php
class Erp_Sales_Model_Order
  extends Erp_Was_Auch_Immer_Model_Abstract
  implements Mage_Sales_Model_Order_Interface
{
  // ...
}

sowie auch jede andere Klasse.

<?php
// ...
public function getEmailFromOrder(Mage_Sales_Model_Order_Interface $order)
{
  return $order->getCustomer()->getEmail();
}
// ...

So können wir jede beliebige Klasse mit bestehenden Code weiterhin verarbeiten, ohne dass uns das Type-Hinting dabei im Weg steht. Was die Methode getCustomer() nun intern macht, ist uns egal, solange ein Objekt vom Typ Mage_Customer_Model_Customer_Interface zurückgegeben wird.

Es ist vielleicht auf den ersten Blick etwas umständlicher, für jeden Typ ein Interface zu schreiben, aber das spart man danach wieder locker ein und bleibt dazu auch in der weiteren Entwicklung seiner Anwendung flexibel.

Ein Kommentar

  1. „welches jedoch noch nicht wirklich seinen Weg in die PHP-Welt gefunden hat.“

    Ob das nun so ist, sei mal dahingestellt. Echtes OOP ist vermutlich genauso wenig oder häufig verbreitet. Liegt letztlich an der Versionsentwicklung und -verbreitung.

    Wie auch immer, ich bin auch großer Fan von Interface-basierten Type-Hints, allerdings muß man schon sehr genau planen (gerade was Erweiterbarkeit anbelangt), denn jede abstrakte Methodendefinition schreibt genau deren Signatur vor und jede spätere Abweichung erzeugt unweigerlich einen Strict-Error.

Schlagwörter: Adapter, Amazon, Animation, Annotations, Anonyme Klasse, Ant, Apache, API, Array, ArrayAccess, Attachment, AutoLoader, Bedienung, Bedingung, Benchmark, Bildbearbeitung, BOM, Bootstrap, Bot, Byte Order Mark, Callback, CamelCase, Canvas, Captcha, Cheatsheet, CLI, Closure, Cloud, CodeSniffer, Community, Comparator, Contest, Controller, Converter, CouchDB, Countable, Cronjob, CSV, CustomLibrary, Custom_Model, Data Mapper, Datei, Datenbank, Datenstruktur, Datentypen, Dating, Decorator, Dekorierer, Design Patterns, Dump, Duplikat, each, Eclipse, Entwicklung, Entwurfsmuster, Enum, Erweiterung, Eventhandling, Exception-Handling, Extension, Factory, Fehler, Flash, Foreach, Formatierung, Formular, Funktion, Futon, Header, HTML5, HTTP, IDE, If, Implementierung, InnoDB, Interceptor, Interface, isset, Iterator, Java, JavaScript, jQuery, Konfiguration, Konsole, Kontrollstruktur, kopieren, Late Static Binding, Layout, Linux, Listeners, Logging, Löschen, Magento, Magic Methods, Marketing, Methode, Model, MVC, MySQL, NetBeans, Objekt, Observable, Observer, OOP, Operator, Parameter, Partnersuche, Performance, PHP, phpMyAdmin, PHPUnit, Plugin, Proxy, Qualitätssicherung, Query, Reflection, Request, Response, Rest-API, Rockstar, Routing, S3, Samba, Scheifen, Schleife, Schutz, Secure Shell, Selbstreferenz, Shop, Sicherheit, Sicherung, Singleton Pattern, SOAP, Sortierung, Sourcecode, Spam, Speicherproblem, Spickzettel, SPL, SSH, Statement, Stellvertreter, Strategy Pattern, Stream, String, Sun VirtualBox, Support, Switch, Symfony, Symfony2, Symfony Live, Tag, Template, Template Method, Ternär Operator, Testing, Thumbnail, Tool, Tour, Twig, Type-Cast, Umwandlung, Underscore, unset, Vererbung, Verzweigung, Video, Videospiel, Virtualisierung, Visitor Pattern, Vorschaubild, walk, Webserver, Webservice, Weiterleitung, Wrapper, Youtube, Zeitsteuerung, Zend Framework, Zend_Cloud, Zend_CodeGenerator, Zend_Http_Client, Zend_Service, Zugriffsmethode