quarta-feira, 15 de junho de 2011

Dependency Injection Design Pattern

Olá,

Hoje falo sobre o design pattern - Dependency injection muito útil por promover baixo acoplamento.

A Injeção de Dependência basicamente diz: não faça uma classe dependente de outro, injete a dependência. Mas como? dirão alguns. É simples. Imagine a
seguinte PHP.

class Car {
   private $driver = null;

   public function __construct() {
      $this->driver = new Driver();
   }

   public function crash() {
      $this->driver->saySomething();
   }
}

Neste momento a classe Car depende do da classe Driver, porque você precisa da classe Driver para instanciar Car. Depois que você fez isso e tudo funciona bem, o seu chefe vem e lhe diz que é preciso distinguir entre os condutores do sexo feminino e motoristas do sexo masculino. Porque se você chamar o crash(), um motorista do sexo feminino seria simplesmente dizer: "você me bateu!" e um motorista do sexo masculino diria algo como: "cara eu tenho uma arma".



Algumas pessoas fariam algo do tipo:
class Car {
    private $driver = null;
    public function __construct($client) {
        if ($client == "A") {
            $this->driver = new MaleDriver();
        } else if ($client == "B") {
            $this->driver = new FemaleDriver();
        }
    }
    public function crash() {
        $this->driver->saySomething();
    }

}

Isto resolve o problema mas agora você tem duas dependências na classe Car. Imagine que para realizar um unit test você precise de utilizar uma classe TestDriver ou uma nova implementação para um ManoDriver.


Uma solução melhor seria utilizar injeção de dependência retirando a obrigação de instanciar o Driver da classe Car e podendo inclusive criar uma classe Factory para facilitar a instanciação.
class Car {

    private $driver = null;

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

    public function crash() {
        $this->driver->saySomething();
    }

}

interface IDriver {

    public function saySomething();
}

class MaleDriver implements IDriver {

    public function saySomething() {
        echo "Eu Tenho Uma Arma";
    }

}

class FemaleDriver implements IDriver {

    public function saySomething() {
        echo "você me bateu!";
    }

}
class ManoDriver implements IDriver{
    public function saySomething() {
        echo "Véi tú mexeu com os cara errado tah ligado!";
    }
}

class Factory {

    public static function createCar($tipo) {
        switch ($tipo) {
            case "male":
                $driver = new MaleDriver();
                break;
            case "female":
                $driver = new FemaleDriver();
                break;
            default:
                throw new Exception("Driver Not Found");
        }
        return new Car($driver);
    }

}

$carWithMaleDriver = Factory::createCar("male");
$carWithMaleDriver->crash();

$carWithFemaleDriver = Factory::createCar("female");
$carWithFemaleDriver->crash();
/*
Sem utilizar factory
*/
$manoDriver = new ManoDriver;
$carWithManoDriver = new Car($manoDriver);
$carWithManoDriver->crash();


Basicamente é assim que funciona a injeção de dependência no entanto, existem frameworks que permitem a que a injeção de dependência seja feita com arquivos xml, yaml, etc.. como Synphony para php.

http://components.symfony-project.org/dependency-injection/trunk/book/

That's all folks.

Um comentário:

  1. Amigo... Muito obrigado por compartilhar essas informações.

    Eu já fazia isso antes, mas não sabia que tinha um design pattern próprio para definir.

    ResponderExcluir