Sommige objecten zijn afhankelijk van andere objecten. Zo heeft een telefoon bijvoorbeeld een sim-kaart nodig om te kunnen bellen. In code bestaan deze afhankelijkheden ook, we noemen deze ook wel dependencies.

Voorbeeld: we hebben een tweetal classes MedewerkerLijst en MedewerkerExporter. De exporteer class pakt  de medewerkerlijst en exporteert deze lijst naar formaat x. We kunnen de MedewerkerExporter class als volgt inrichten:

class MedewerkerExporter {
  
  protected $lijst;

  public function __construct() {
    $this->lijst = new MedewerkerLijst();
  }

  public function exporteer() {
    foreach( $this->lijst->get() as $medewerker ) {
       // Doe wat medewerker logica...
    } 

    // Exporteren naar XML.
  }
}

In het bovenstaande voorbeeld is de MedewerkerLijst een dependency van het de class MedewerkerExporter. Het voorbeeld werkt prima, alleen het is niet herbruikbaar.

Dependency injection

We kunnen de lijst bijvoorbeeld ook meegeven aan de constructor. Dit heet dependency injection.  Bij dit principe hoeft een object niet perse meegegeven worden aan de constructor, het mag natuurlijk ook een afzonderlijke methode zijn.

Als iets als parameter wordt meegegeven, dan spreken we van dependency injection. Dus het kan ook een string zijn. 

Het voordeel van dependency injection is dat je een bestaand object kan meegeven. Zo kan de Medewerklijst een voegToe methode hebben om een medewerker toe te voegen.

<?php
$medewerkerLijst = new MedewerkerLijst();
$medewerkerLijst->voegToe( 'Andy' );

$exporteer = new MedewerkerExporter( $medewerkerLijst ); 
$exporteer->exporteer();

In theorie kunnen we nu elke lijst exporteren. Zolang het meegegeven object de gewenste methodes heeft die nodig zijn in de MedewerkerExporter class, dan gaat het goed. 

Wat nu als we ook de inventaris willen exporteren? We kunnen dan een InventarisLijst en een InventarisExporter maken. Voor het lijst stukje zal misschien iets andere code gebruikt worden, maar een groot deel van de exporter is waarschijnlijk hetzelfde.

  public function exporteer() {
    foreach( $this->lijst->get() as $inventaris ) {
       // Doe wat inventaris logica...
    } 

    // Exporteren naar XML.
  }

Gebruik interfaces

Voor het schrijven van herbruikbare code kunnen we interfaces gebruiken. We kunnen het beste kijken naar welke patronen onze code heeft. In ons geval willen we een lijst exporteren: de ene keer is het inventaris, de andere keer zijn het medewerkers. We hebben dus een lijst interface nodig.

interface Lijst {
   public function get();

   public function voegToe( $item );
}

Onze exporteer class wordt generieker, dus die kunnen we gewoon Exporter noemen.

class Exporter {
  
  protected $lijst;

  public function __construct( $lijst ) {
    $this->lijst = $lijst;
  }

  public function exporteer() {
    foreach( $this->lijst->get() as $item ) {
       // Doe wat item logica...
    } 

    // Exporteren naar XML.
  }
}

Interface specifiek afdwingen

Nu kunnen we feitelijk alles exporteren. In de meeste gevallen weten we dat dit, omdat we de code zelf schrijven.  Maar we willen eigenlijk afdwingen dat de lijst die de Exporter krijgt wel van het juiste type is.

We kunnen het variable type afdwingen. Dit heeft type declaration, ook wel bekend als type hinting

  public function __construct( Lijst $lijst ) {
    $this->lijst = $lijst;
  }

Bij de parameter geven we gewoon de naam van de interface. Als de class bij het instantiëren iets anders krijgt, dan zal PHP een fout geven. 

We kunnen type hints geven voor verschillende types. Het mag een interface, abstracte class of gewone class zijn. Maar ook array en self zijn toegestaan. Vanaf PHP 7.0 is het ook mogelijk om callable, bool, float, int, iterable (7.1) en string als type hint te geven.