Construindo objetos complexos com Builders (parte 2 de 2)

Olá, pessoal

Nesta segunda parte do artigo sobre o design pattern Builder, vou mostrar o exemplo dado na primeira parte (<<leia aqui>>), utilizando o conceito de interface fluente. Também falarei sobre a aplicação de builders para criação de dados de testes (Test Data Builders).

EXEMPLO – INTERFACE FLUENTE

Começamos alterando a interface do builder abstrato “CriadorDeGuerreiro”, de forma que os métodos que constroem partes do objeto-produto agora retornem o próprio builder:

public abstract class CriadorDeGuerreiro
{
    protected Guerreiro _guerreiro;
    public Guerreiro ObterGuerreiro()
    {
        return _guerreiro;
    }

    // retorno alterado de 'void' para 'CriadorDeGuerreiro'
    public abstract CriadorDeGuerreiro ComEspada();
    public abstract CriadorDeGuerreiro ComArmadura();
    public abstract CriadorDeGuerreiro ComArco();
}

Consequentemente, devemos alterar nossos builders concretos para atender a interface acima. Mostrarei como fica apenas com um deles:

public class CriadorDeGuerreiroMedieval : CriadorDeGuerreiro
{
    public CriadorDeGuerreiroMedieval()
    {
        _guerreiro = new GuerreiroMedieval();
    }

    public override CriadorDeGuerreiro ComEspada()
    {
        _guerreiro.EscolherEspada("espada medieval");
        return this;
    }

    public override CriadorDeGuerreiro ComArmadura()
    {
        _guerreiro.ColocarArmadura("armadura medieval");
        return this;
    }

    public override CriadorDeGuerreiro ComArco()
    {
        _guerreiro.EscolherArco("arco medieval");
        return this;
    }
}

Percebam que agora os métodos que montam parte do produto retornam o próprio builder (return this). Se vocês não conhecem o conceito de interface fluente, verão o porquê disso mais abaixo.

A última modificação a ser feita agora é no director “Exercito” para construir o objeto-produto de forma fluente:

public class Exercito
{
    public void ConstruirGuerreiro(CriadorDeGuerreiro criadorDeGuerreiro)
    {
        // montagem do produto de forma fluente
        criadorDeGuerreiro.ComArco().ComArmadura().ComEspada();
    }
}

Notem que agora, como cada método retorna o próprio builder (o próprio objeto corrente), podemos chamá-los em cadeia (method chaining), na ordem que quisermos e quais deles quisermos, é claro. (Para exercitarmos esse código, podemos usar o código da aplicação console mostrado na parte 1 deste artigo, sem nenhuma modificação.)

TEST DATA BUILDERS

Uma boa aplicação para os builders é usá-los para criar objetos em cenários de testes (e é a forma como eu venho utilizando este padrão em meus últimos projetos).

A implementação do padrão pode ser mais simplificada em um projeto de testes. Como queremos apenas variar as partes criadas do objeto de acordo com o cenário de teste e não COMO cada parte é criada, não precisamos de um builder abstrato. Criamos apenas o builder concreto para o objeto e pronto!

Vejamos alguns exemplos:

[Test]
public void um_teste_qualquer()
{
    var pedido = PedidoBuilder.UmPedido().SemNenhumItem().Build();
    //......
}

[Test]
public void outro_teste()
{
    //......
    var pedido = PedidoBuilder.UmPedido()
                              .ComItem(produto1, 10)
                              .ComItem(produto2, 20)
                              .ComDescontoDe(0.5)
                              .Build();
    //......
}

Podemos ver nos exemplos que ganhamos em legibilidade e em flexibilidade, pois podemos fazer a combinação de métodos que for mais adequada para cada teste. Também ganhamos em facilidade de manutenção,  já que quando alteramos o objeto-produto (principalmente seu construtor), só precisaremos corrigir no builder, ao invés de sairmos fazendo o ajuste em dezenas de testes.

Além disso, PedidoBuilder é implementado de uma forma levemente diferente daquela mostrada nos exemplos do Guerreiro (considerações nos comentários):

public class PedidoBuilder
{
    private double _desconto;

    // PRIMEIRO: TORNAMOS O CONSTRUTOR PRIVADO
    private PedidoBuilder() {}

    // SEGUNDO: CRIAMOS UM FACTORY METHOD ESTÁTICO PARA DEIXAR MAIS EXPLÍCITO
    // NO TESTE O QUE ESTÁ SENDO CRIADO
    public static PedidoBuilder UmPedido()
    {
        return new PedidoBuilder();
    }

    // TERCEIRO: GUARDAMOS A PARTE CRIADA TEMPORARIAMENTE
    public PedidoBuilder ComDescontoDe(double desconto)
    {
        _desconto = desconto;
        return this;
    }

    // QUARTO: O PRODUTO SÓ É CRIADO NO FINAL, AO CHAMARMOS O MÉTODO QUE O RECUPERA
    public Pedido Build()
    {
        var pedido = new Pedido(_desconto);
        return pedido;
    }
}

Simples, não é?

CONCLUSÃO

Nesta continuação do artigo sobre o design pattern Builder, vimos outras formas de implementação do mesmo, assim como uma forma de aplicá-lo em cenários de testes. Implementados de forma fluente, facilitam na implementação, legibilidade e manutenção dos testes.

Espero que tenham gostado.

[]s e até a próxima!

———————————————————
Referência:
Freeman, Steve. Pryce, Nat. 2010. Growing Object-Oriented Software, Guided By Tests. Addison-Wesley.

Anúncios

Um comentário em “Construindo objetos complexos com Builders (parte 2 de 2)

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