Padrões de DI: Property Injection

Olá, galera

Vamos continuar a série “Padrões de DI” mostrando outra forma de injetar dependências, conhecida por Property Injection. Seguimos os mesmos moldes do post anterior, trazendo a definição do padrão, quando usá-lo e como implementá-lo.

1. O QUE É

Property Injection – também chamado de Setter Injection – é um padrão de DI onde a dependência é fornecida por meio de uma propriedade com o setter público (ou ainda por meio de um setter method).

2. QUANDO USAR

Ao contrário de Constructor Injection, Property Injection é melhor utilizado quando a dependência é opcional e, além disso, a classe consumidora possui uma opção default (*) para a dependência.

(*) Mark Seemann, em seu livro de DI, chama esta opção de local default, por se tratar de uma dependência que, por coesão, encontra-se no mesmo assembly da classe consumidora.

3. COMO IMPLEMENTAR

Vejamos como fica a implementação mais básica do padrão:

public class Funcionario
{
    // setter público para a dependência possa ser alterada
    public ICalculadorDeBonus Calculador { get; set; }

    public Funcionario()
    {
        // "local default" atribuido no construtor
        Calculador = new BonificacaoPadrao();
    }

    // usando a dependência
    public decimal Bonificacao()
    {
        return Calculador.CalcularBonus();
    }
}

Alguns detalhes:

1. Poderíamos, como uma leve variação, ter utilizado um field ao invés da propriedade e ter criado um método para setar a dependência (como void AlterarFormaDeCalculo(ICalculadorDeBonus  novoCalculador)).

2. É recomendado utilizar uma guard-clause – de forma semelhante ao que fizemos para demonstrar o Constructor Injection no post anterior – para evitar que seja atribuido null à dependência.

3. BonificacaoPadrao é um local default, ou seja, uma classe conceitualmente pertencente ao mesmo assembly de Funcionario (neste caso, um assembly que contém entidades do domínio). Se estivéssemos instanciando um DAO, por exemplo, estaríamos ferindo tudo que está sendo discutido nesta série, pois estaríamos acoplando nosso domínio com uma dependência concreta de uma camada mais volátil (no caso, persistência).

4. Notem também que o exemplo faz uso do design pattern Strategy. Para saber mais sobre ele, dê uma lida <nesse meu outro artigo>.

4. PODE FICAR MAIS COMPLEXO

Pode haver necessidades que exijam uma implementação mais robusta do padrão.

Por exemplo, se necessitarmos que a dependência, após “setada”, não seja mais alterada durante todo o ciclo de vida do objeto, seremos obrigados a adicionar mais uma guard-clause no setter para evitar uma nova atribuição.

Poderíamos também ter que tratar thread-safety, caso houvesse código concorrente acessando o objeto e tentando setar a dependência ao mesmo tempo.

5. CONCLUINDO

Usamos Property Injection quando a dependência for opcional e tivermos um local default definido.

Sua implementação pode ser mais complexa do que a implementação do padrão Constructor Injection e devemos estar mais atentos para não cairmos em uma cilada.

Até agora, vimos nesta série as duas formas mais comuns de se injetar dependências. Nos próximos posts, falaremos de outros padrões mais incomuns.

Aguardo vocês!

O que acharam? Comentem aí!

Posts anteriores da série na tag <dependency injection>.

Anúncios

7 comentários em “Padrões de DI: Property Injection

  1. Robson, uma pergunta que surgiu ao ler o post: Qual a sua opinião se ao invés de atribuirmos manualmente uma implementação default, utilizássemos um Service Locator Pattern ? Apesar de ser considerado por muitos um anti-pattern, mas nesse caso, evitaríamos uma instanciação desnecessária de um objeto no construtor, que depois será sobreposto pelo container de DI, no momento em que o container faz a atribuição. Seria algo do tipo: Calculador = ServiceLocator.GetInstance() ?? new BonificacaoPadrao();

    1. Blz, Jone?
      Mas vc nao instanciou do mesmo jeito ? Não faz muito sentido escolher entre um ou outro. Ou voce dá new ou deixa o container escolher (e ele saberá te dar alguma instancia).

      Bom, supondo que a opção seja remover o ‘new’ e setar o local default usando o container, no caso, como um service locator, sinceramente não gosto muito da opção. Gosto menos ainda de usá-lo em uma entidade de domínio, pois entidades devem conversar entre si e não com ‘servicos técnicos’. Além disso, o uso do service locator tende a se espalhar pelas classes escondendo as dependencias da mesma e também, caso haja intenção de reuso da classe/módulo, você será obrigado a arrastar o service locator junto.

      No caso do exemplo, o acoplamento com o local default é “do bem” uma vez que ambos fazem parte do mesmo módulo e o Open/Closed Principle continua preservado pois a classe depende da interface (ICalculador..) e há um ponto de troca da implementação (extensibilidade). Por fim, a dependência é um mero adjustment (termo do livro GOOSGBT), no caso, um strategy para permitir estender a funcionalidade da classe, portanto seu construtor é normalmente bem simples (sem parametros).

      IMHO, é isso, qualquer coisa trocamos mais ideias!
      []s e obrigado por comentar.

      1. Obrigado por responder Robson, ficou bem claro e concordo com seu ponto de vista de não criar um acoplamento ao service locator. A minha preocupação em instanciar desnecessariamente um objeto no construtor que pode não ser utilizado depois, visto que há uma grande possibilidade de meu container de DI injetar outra instância após a construção do objeto que está sendo resolvido via Property Injection, ainda persiste (não haveria essa preocupação caso estivesse utilizando Constructor Injection). Uma forma de matar essa minha preocupação, e já resolvendo o problema de instâncias nulas, seria a de usar o getter e o setter da propriedade, em conjunto com um backing field, tornando a instanciação do local default atrasada “lazy”, caso o container não tenha injetado nenhuma dependência. Ficaria mais ou menos assim: https://gist.github.com/jpolvora/a9e717e543826f86bc80 . O que acha ?

      2. É por aí mesmo. Ainda poderia disparar excecao se tentasse setar null e poderia ter outra guard-clause para evitar setar de novo, se fosse o caso.
        Quanto mais robusto mais complexo.
        Lembrando que, se há duvidas entre qual padrão usar ou se a dependencia é obrigatória, é melhor ir de Constructor Injection que é muito mais simples.
        []s

    1. Sabemos que a velha máxima da OO nos diz que quem usa o objeto não deve cria-lo. Essa é uma boa prática a ser seguida (o que podemos ver nesta série, inclusive).

      Mas este é um cenário específico, onde estamos apenas inicializando um dos atributos do objeto com um valor default, que assumo ser usado somente por ele (Funcionario) e que pertence ao mesmo assembly.
      Além disso, independente de ter criado ou não o objeto dentro da classe, o uso do calculador não faz que o objeto perca coesão. Ele apenas está delegando parte do serviço (calcular bonus) para um cara auxiliar, em troca de simplicidade e flexibilidade.

      Respondendo a pergunta: não, o simples uso do new não deixa a classe com mais de uma responsabilidade.

      Qualquer coisa, estamos aí pra discutir.

      []s e obrigado por comentar.

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s