on Feb 4th, 2010Dependency Injection med PHP

Vid utveckling vill man alltid skapa så löst kopplade system som möjligt. Detta speciellt om man skriver enhetstester eller tillämpar testdriven utveckling. För att underlätta för sig själv skall man försöka följa de “SOLID”a principerna så gott det går.

D:et i SOLID står för Dependency Inversion, och betyder, enkelt sagt, att en klass inte skall vara hårt kopplad till en annan klass. För att lösa detta brukar man tillämpa en teknik som ofta kallas dependency injection, vilket innebär att klasser som skall instansieras får sina beroenden från den som instansierar klassen.

Ett exempel på en klass som inte följer Dependency Inversion är följande:

<?php
class HtmlParser
{
    public function GetTitle($url)
    {
        $downloader = new HtmlDownloader();
        $content = $downloader->download($url);
       
        $title = preg_match(‘~<title>(.*?)</title>~’, $content, $match);
       
        return $match;
    }
}
?>
 

Denna klassen har ett beroende av HtmlDownloader, vilket innebär att man inte kan återanvända klassen HtmlParser från ett annat ställe. Vad händer om vi istället för att ladda ner filen via HTTP vill hämta innehållet från en lokal fil? Då måste vi antingen skapa en ny klass för detta, eller en ny metod för att göra detta.

En annan, och bättre, lösning på detta är att låta klassen HtmlParser ta en klass som implementerar ett gemensamt interface, som inparameter i konstruktorn, till exempel:

<?php
interface IHtmlProvider
{
    function getHtml($path);
}

class LocalFileHtmlProvider implements IHtmlProvider
{
    function getHtml($path)
    {
        // Läs och returnera från filsystemet
    }
}

class RemoteHtmlProvider implements IHtmlProvider
{
    function getHtml($path)
    {
        // Ladda ner via HTTP
    }
}
?>
 

Detta gör att vi kan modifiera vår parser till att se ut enligt följande:

<?php
class HtmlParser
{
    private $provider;
   
    public function __construct(IHtmlProvider $provider)
    {
        $this->provider = $provider;
    }
   
    public function GetTitle($path)
    {
        $content = $this->provider->getHtml($path);
       
        $title = preg_match(‘~<title>(.*?)</title>~’, $content, $match);
       
        return $match;
    }
}
?>
 

Nu kan vi enkelt byta källa för vår html, och återanvända koden precis var vi vill, så länge får källa implementerar IHtmlProvider. Vill vi bygga en FtpHtmlProvider är detta inga som helst problem.

I ett kommande inlägg kommer jag visa en teknik som kallas för Inversion of Control, och är nära besläktat med Dependency Injection.

Externa länkar med mer informtion:

Wikipedia om Dependency Injection

Wikipedia om Dependency Inversion Principle

Wikipedia om SOLID Principles

Rekommenderas:
kleine schilder

RSS

Lämna en kommentar