Imaster

Os valores de configuração são dependências também

Como parte do meu trabalho de consultoria, tenho a oportunidade de rever os lotes de diferentes bases de código de diferentes atualizações. Uma coisa que eu tenho notado com algumas bases de código mais novas é que elas muitas vezes “chegam” a partir de dentro de uma classe para recuperar os valores de configuração, em vez de injetar esses valores para a classe do lado de fora. Isto é, elas usam um equivalente a globais ou serviço de localização para ler a configuração, em vez de usar injeção de dependência.

Aqui está um exemplo genérico:

<?php
class Db
{
    // backend type, hostname, username, password, and database name
    protected $type, $host, $user, $pass, $name;

    public function __construct()
    {
        $this->type = getenv('DB_TYPE');
        $this->host = getenv('DB_HOST');
        $this->user = getenv('DB_USER');
        $this->pass = getenv('DB_PASS');
        $this->name = getenv('DB_NAME');
    }

    public function newConnection()
    {
        return new PDO(
            "{$this->type}:host={$this->host};dbname={$this->name}",
            $this->user,
            $this->pass
        );
    }
}
?>

O exemplo segue a prática moderna de manter informações sensíveis como variáveis ​​de ambiente. Exemplos semelhantes usam chaves $_ENV ou $_SERVER em vez de getenv(). O efeito, no entanto, é uma espécie de global ou de serviço localizador: a classe está alcançando fora do seu escopo para recuperar valores que necessita para seu próprio funcionamento. Da mesma forma, não se pode dizer do lado de fora da classe de quais valores de configuração ela depende.

O exemplo seguir é melhor?

<?php
class Db
{
    public function __construct()
    {
        $this->type = Config::get('db.type');
        $this->host = Config::get('db.host');
        $this->user = Config::get('db.user');
        $this->pass = Config::get('db.pass');
        $this->name = Config::get('db.name');
    }
}
?>

Até onde posso dizer, isso é uma variação do mesmo tema. O objeto Config genérico age como um singleton global para realizar a configuração para cada necessidade possível; ele está agindo como um localizador de serviço estático. Enquanto o serviço de localização é a inversão de controle, em muitos aspectos é inferior à injeção de dependência. Como antes, a classe está alcançando fora do seu escopo para recuperar valores de que ela depende.

E se nós injetarmos o objeto Config genérico como a seguir?

<?php
class Db
{
    public function __construct(Config $config)
    {
        $this->type = $config->get('db.type');
        $this->host = $config->get('db.host');
        $this->user = $config->get('db.user');
        $this->pass = $config->get('db.pass');
        $this->name = $config->get('db.name');
    }
}
?>

Esse está um pouco melhor; pelo menos agora podemos dizer que a classe Db precisa de uma configuração de algum tipo, embora ainda não possamos dizer exatamente quais os valores que ela precisa. Isao é o mesmo que injetar um localizador de serviço.

Tendo visto todos esses exemplos, e outros similares, em bases de código reais, concluo que os valores de configuração devem ser tratados como qualquer outra dependência, e injetados pelo construtor. Eu sugiro esta abordagem:

<?php
class Db
{
    public function __construct($type, $host, $user, $pass, $name)
    {
        $this->type = $type;
        $this->host = $host;
        $this->user = $user;
        $this->pass = $pass;
        $this->name = $name;
    }
}
?>

Simples, claro, óbvio e fácil de testar. Se você usar um container de injeção de dependência de algum tipo, deve ser trivial ler variáveis ​​de ambiente e passá-las para a classe Db no momento da construção. (Se o seu container DI não suporta esse tipo de coisa, você deve considerar o uso de um sistema de container mais poderoso.)

Como alternativa, eu acho que o seguinte pode ser razoável em alguns casos:

<?php
class DbConfig
{
    // backend type, hostname, username, password, and database name
    protected $type, $host, $user, $pass, $name;

    public function __construct($type, $host, $user, $pass, $name)
    {
        $this->type = $type;
        $this->host = $host;
        $this->user = $user;
        $this->pass = $pass;
        $this->name = $name;
    }

    public function getDsn()
    {
        return "{$this->type}:host={$this->host};dbname={$this->name}";
    }

    public function getUser()
    {
        return $this->user;
    }

    public function getPass()
    {
        return $this->pass;
    }
}

class Db
{
    protected $dbConfig;

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

    public function newConnection()
    {
        return new PDO(
            $this->dbConfig->getDsn(),
            $this->dbConfig->getUser(),
            $this->dbConfig->getPass()
        );
    }
}
?>

Nesse exemplo, o DbConfig gerencia um conjunto de valores de configuração injetados para que o objeto Db trate sua própria configuração como uma preocupação em separado. Entanto, essa abordagem é um pouco demasiada indireta e aberta a abusos para o meu gosto na maioria das vezes. A tentação é começar a colocar mais e mais dentro do objeto DbConfig, e você acaba com um mini-serviço de localizador.

Para resumir: os valores de configuração são dependências; portanto, injete valores de configuração do jeito que você faria com qualquer outra dependência.

UPDATE: Stephan Hochdörfer escreveu no Twitter: “Eu provavelmente mudaria um pouco a frase: Os valores de configuração devem ser tratado como deps. Não tenho certeza se você pode dizer que eles são deps ;)”. Bem citado esse ponto, embora possa ser uma distinção sem diferença. Se a classe não pode funcionar adequadamente sem um valor especial, se esse valor é um escalar ou um objeto, eu acho que é justo dizer que a classe é dependente desse valor.

***

Paul M. Jones faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://paul-m-jones.com/archives/6203

Mensagem do anunciante:

Conheça a Umbler, startup de Cloud Hosting por demanda feita para agências e desenvolvedores. Experimente grátis!

Powered by WPeMatico