Mão na massa: Builder

Quase encerrando a lista de Padrões de Criação, vamos exemplificar agora o padrão Builder!

Problema

Antes de falar do problema é preciso dizer que este padrão é bem parecido com o Factory Method e o Abstract Factory, então vamos analisar o mesmo exemplo para ver melhor as diferenças e semelhanças entre eles.

O problema é que precisamos modelar um sistema de venda de carros para uma concessionária. Queremos que o sistema seja flexível, para adição de novos carros, e de fácil manutenção. Vimos que com os padrões Factory Method e o Abstract Factory podemos alcançar este resultado de maneira bem simples.

Vamos então apresentar o padrão Builder e ver como ele funcionária nesta situação.

Builder

A intenção do padrão segundo [1]:

“Separar a construção de um objeto complexo de sua representação de modo que o mesmo processo de construção possa criar diferentes representações.”

Separar a construção da representação segue a mesma ideia dos padrões Factory Method e Abstract Factory. No entanto o padrão Builder permite separar os passos de construção de um objeto em pequenos métodos. Então vamos direto ao código para ver o que muda.

No padrão Builder temos também uma interface comum para todos os objetos que constroem outros objetos. Essa interface Builder define todos os passos necessários para construir um objeto. Vamos então ver o nosso objeto produto:

public class CarroProduct {
	double preco;
	String dscMotor;
	int anoDeFabricacao;
	String modelo;
	String montadora;
}

Nada mais é do que uma estrutura de dados (Lembre: estrutura de dados != objeto) que armazena as informações de um carro. A nossa classe Builder vai possui um método para construir cada um dos dados do nosso Produto:

public abstract class CarroBuilder {

	protected CarroProduct carro;

	public CarroBuilder() {
		carro = new CarroProduct();
	}

	public abstract void buildPreco();

	public abstract void buildDscMotor();

	public abstract void buildAnoDeFabricacao();

	public abstract void buildModelo();

	public abstract void buildMontadora();

	public CarroProduct getCarro() {
		return carro;
	}
}

Nesta classe temos o carro que será construído, os passos para sua construção e um método que devolve o carro construído. Apesar da aparente semelhança com o padrão Template Method, que deixa as subclasses definirem alguns métodos do algoritmo, na classe Builder não existe um “algoritmo” bem definido, o algoritmo será definido em outro lugar. Então vejamos agora as classes Builder concretas:

public class FiatBuilder extends CarroBuilder {

	@Override
	public void buildPreco() {
		// Operação complexa. 
		carro.preco = 25000.00;
	}

	@Override
	public void buildDscMotor() {
		// Operação complexa.
		carro.dscMotor = "Fire Flex 1.0";
	}

	@Override
	public void buildAnoDeFabricacao() {
		// Operação complexa.
		carro.anoDeFabricacao = 2011;
	}

	@Override
	public void buildModelo() {
		// Operação complexa.
		carro.modelo = "Palio";
	}

	@Override
	public void buildMontadora() {
		// Operação complexa.
		carro.montadora = "Fiat";
	}
}

Na classe FiatBuilder nós “personalizamos” o carro, com as informações da Fiat. Note os comentários “// Operação complexa”. Antes da atribuição do preço, por exemplo, poderíamos realizar todo o cálculo necessário, por exemplo, buscando o valor no banco de dados, calcular impostos, desvalorização, entre outras operações. Essa é a ideia principal do padrão Builder, dividir em pequenos passo a construção do objeto.

Agora vamos ver a classe chamada de Director, ela utiliza a estrutura do Builder para definir o algoritmo de construção do Produto.

public class ConcessionariaDirector {
	protected CarroBuilder montadora;

	public ConcessionariaDirector(CarroBuilder montadora) {
		this.montadora = montadora;
	}

	public void construirCarro() {
		montadora.buildPreco();
		montadora.buildAnoDeFabricacao();
		montadora.buildDscMotor();
		montadora.buildModelo();
		montadora.buildMontadora();
	}

	public CarroProduct getCarro() {
		return montadora.getCarro();
	}
}

Dado um Builder, a classe vai executar os métodos de construção, definindo assim o algoritmo de construção do carro, e depois devolve o carro. O código cliente vai lidar apenas com o Director, toda a estrutura e algoritmos utilizados para construir o carro ficarão por debaixo dos panos.

	public static void main(String[] args) {
		ConcessionariaDirector concessionaria = new ConcessionariaDirector(
				new FiatBuilder());

		concessionaria.construirCarro();
		CarroProduct carro = concessionaria.getCarro();
		System.out.println("Carro: " + carro.modelo + "/" + carro.montadora
				+ "\nAno: " + carro.anoDeFabricacao + "\nMotor: "
				+ carro.dscMotor + "\nValor: " + carro.preco);

		System.out.println();

		concessionaria = new ConcessionariaDirector(new VolksBuilder());
		concessionaria.construirCarro();
		carro = concessionaria.getCarro();
		System.out.println("Carro: " + carro.modelo + "/" + carro.montadora
				+ "\nAno: " + carro.anoDeFabricacao + "\nMotor: "
				+ carro.dscMotor + "\nValor: " + carro.preco);
	}

Veja que foi bastante fácil mudar o produto, apenas precisamos utilizar um novo Builder para construir o produto que quisermos. A duplicação de código no cliente foi intencional, para mostrar que o cliente pode manipular o produto. Caso queira basta colocar o código que exibe as informações do carro em um método da classe Director, escondendo do cliente a estrutura do Produto.

Veja o resultado da utilização do padrão Builder:

Um pouco de teoria:

Já mostramos a principal vantagem do padrão Builder, separar em pequenos passos a construção do objeto complexo. Vamos então comparar, como foi dito no início, o padrão Builder com o Abstract Factory e o Factory Method.

Todos eles servem para criar objetos, além disse escondem a implementação do cliente, tornando o programa mais flexível. No entanto, no padrão Builder, não existe o conceito de vários produtos ou de famílias de produtos, como nos outros dois padrões.

Volte ao código e veja que definimos apenas um produto: Carro. Cada fábrica do Builder vai personalizar o seu Produto nos pequenos passos de construção. Essa diferença fica mais evidente ao comparar os diagramas UML do Facottory Method, Abstract Factory e Bulder. A estrutura do Builder é bem menor que a dos outros dois.

Outra diferença é que o Builder foca na divisão de responsabilidades na construção do Produto. Enquanto nos padrões Abstract Factory e Factroy Method tínhamos apenas o método criarCarro(), que deveriam executar todo o processo de criação e devolver o produto final, no padrão Builder nós definimos quais os passos devem ser executados (na classe Builder) e como eles devem ser executados (na classe Director).

Várias classes Director também podem reutilizar classes Builder. Como o Builder separar bem os passos de construção, o Director tem um controle bem maior sobre a produção do Produto.

Um problema com o padrão é que é preciso sempre chamar o método de construção para depois utilizar o produto em si. No nosso exemplo essa responsabilidade foi dada ao código cliente. No entanto a classe Director poderia realizar todas as chamadas em um único método e depois apenas retornar o produto final ao cliente.

Código fonte completo

O código completo pode ser baixado no seguinte repositório Git: https://github.com/MarcosX/Padr-es-de-Projeto.

Os arquivos estão como um projeto do eclipse, então basta colocar no seu Workspace e fazer o import.

Se gostou do post compartilhe com seus amigos e colegas, senão, comente o que pode ser melhorado. Encontrou algum erro no código? Comente também. Possui alguma outra opinião ou alguma informação adicional? Comenta ai! 😀

Referências:

[1] GAMMA, Erich et al. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos.

20 comentários sobre “Mão na massa: Builder

  1. Baixei o Arquivo e não encontrei o Builder!!!!!! 😦

  2. O Builder ainda não esta la, poderia adicioná-lo Marcos, por favor!

  3. Parabens!! Irei fazer uma prova de Padrões de criação e essas suas postagens me exclareceu e muito. E vou usar as outras para estudar para as proximas provas.

  4. A sua explicação é muito boa, porém você usa o nome ‘montadora’ quando fala de ‘modelos de carro’. Fico muito confuso com isso, por exemplo: [… ] public class FiatBuilder extends CarroBuilder […], nesse caso eu fico pensando: – será que ele está falando de carro ou de montadora? Preciso reler os exemplos muitas vezes para entender, porém ainda fico em dúvida. Nesse caso, eu preciso de um builder para cada montadora ou para cada modelo de carro? Se eu tiver os carros: Uno, Palio e Siena, preciso criar um builder para cada um, ou o builder da montadora Fiat já resolve o problema? Senão, como seria?

    Acho que esse é um ponto a melhorar, no mais, está excelente a série!

  5. Marcos,

    Analisando um diagrama sobre Builder na wikipedia (http://pt.wikipedia.org/wiki/Builder#mediaviewer/File:Builder_UML_class_diagram.svg) observei que a classe Director não recebe em seu construtor a classe/interface da fábrica de objetos. Porque seu exemplo está diferente?

    • A implementação do padrão muda um pouco dependendo de qual funcionalidade da linguagem você utiliza e até mesmo de qual linguagem utilizada. Mesmo com uma pequena mudança no contexto de aplicação do padrão ainda é possível utilizá-lo. O bom de você ter olhado exemplos em outros lugares é que você pode comparar os dois e extrair a essência do padrão. Não dá pra criar um diagrama igual para todas as utilizações dos padrões (em alguns casos talvez), sempre o contexto precisa ser levado em consideração.

      • Preciso saber se é possível a classe cliente passar parâmetros para a classe diretor, se sim, como fazer isso? Pode ser através do método construirCarro?

  6. Preciso saber se é possível a classe cliente passar parâmetros para a classe diretor, se sim, como fazer isso? Pode ser através do método construirCarro?

  7. Marcos, observando este padrão e o Abstract Factory, consegui imaginar como seria a integrações de ambos, no caso, o Builder para definir os atributos do objeto e o Abstract Factor para agrupá-los por fatores gerais comuns. Isso está correto, ou quando se usa um não faz sentido usar outro?

    • Oi Rafael, não tem problemas em utilizar dois padrões o único problema é que você vai ter muito código pra fazer pouca coisa. O ideal é tentar minimizar o uso de padrões para simplificar o seu design.
      No exemplo que você deu acho que seria melhor tentar criar vários builders (caso os parâmetros mudem bastante) ou vários métodos fábrica (caso os parâmetros não mudem muito mas ainda assim precise de variações do objeto).
      Eu escrevi um pouco sobre como evoluir com padrões de projeto aqui: https://leanpub.com/primeiros-passos-com-padroes-de-projeto/read.

  8. Marcos, estava observando esse padrão e seria possível dentro desse builder termos classes de serviços e DAOs por exemplo já que ele realiza coisas complexas? Outro ponto é o que chamam de builder hoje no qual o cliente acessa diretamente o builder diretamente com Fluent Interface para criar um objeto. Para mim nesse caso talvez devessem até chamar de outro nome pois é muito diferente do padrão do GOF né. O que você acha?

    • Oi Dênis, o ponto principal do Builder é ter maior flexibilidade com a criação de objetos, então a pergunta que você precisa fazer é: Meu DAO (ou qualquer que seja o objeto) precisa dessa flexibilidade?
      Muita gente utiliza o builder sem precisar dessa flexibilidade, só pra ter um Fluent Interface e evitar um construtor gigante (como o que você comentou). Mas isso na verdade só esconde o problema de ter um objeto com muitas dependências e nem é uma aplicação do Builder.

  9. […] dificultar sua implementação quanto dificultar o entendimento dos outros. O coitado do Builder que o […]

  10. Marcos qual é o nome da classe que tem o main ?

Deixe um comentário