Série DesignPattern:Builder

Olá Pessoal,

No post da série de hoje vamos conhecer mais um Design Pattern: o Builder. Ele pertence aos Padrões de criação. No post vamos entender melhor o objetivo desse padrão e teremos um exemplo prático para desenvolver e entender à nível de código Java como as coisas acontecem, ou seja, será uma pequena prova de conceito.  Como sempre buscarei usar uma linguagem informal e direta.

Lets go…

Builder

É uma interface que define os passos para criação do produto.

Exemplo:

“para gerar um boleto informe:  sacado,cedente, valor, vencimento, NossoNro e após que você informar tudo isso terá um boleto pronto (ou seja, retorne um boleto pronto)”

Permitir separar a construção do objeto e sua representação, possibilitando a construção passo-passo do objeto, esse é o objetivo do Builder.

Exemplo: Boleto Bancário

Cada banco possui informações específicas no boleto, mas todos devem seguir (implementar) as regras básicas FEBRABAN.

O padrão Builder é formado por:

  • Product (boleto): a interface que define os objetos a serem construídos pelo Builder. Podemos usar um exemplo tentando responder a seguinte pergunta: o que um boleto precisa?
    Resposta: sacado, cedente, código de barras, valor e vencimento. (claro que há outras informações requeridas, mas me limitei a essas).
  • Builder: é uma interface que define os passos para criação do produto.
  • ConcreteBuilder: cria o Product (boleto).
  • Director: chama o builder (qualquer implementação de Builder) para construir o produto. “Ei, passe as informações que teremos no boleto para o JoãoBuilder que ele vai gerar um para você.”

Praticando…
Nada melhor que praticar não é? Então a seguir vamos ver como ter o padrão Builder em uma aplicação Java.

Criaremos um projeto conforme a imagem a seguir:

Passo 1
O primeiro passo é pensar como você pode gerar N boletos de maneira simples e de fácil manutenção. Há N bancos no mercado: Itaú, Bradesco, Banco do Brasil etc, e cada dia novos bancos vão surgindo e talvez sua aplicação terá que dar suporte àquele novo banco que, quando você desenvolveu a aplicação, não existia. Mas você quer fazer isso da forma mais simples possível. Adicionar um novo banco emissor de boletos não pode ser algo custoso para seu projeto.
Então vamos ter:

  • – uma interface chamada Boleto: interface tem como objetivo de dizer a cada banco o que um boleto tem e deve ser preenchido. Por exemplo:
    Um boleto deve ter: quem é o sacado, cedente, uma data de vencimento e um valor”. Independente do banco ou produto, um boleto tem quer ter essas informações preenchidas, do contrário não temos um boleto pronto para uso.

package com.camilolopes.interfaces;

import java.util.Calendar;
/*
* interface que define o que um boleto
* deve ter. qualquer banco quer tem interesse em gerar
* um boleto deve informar quem é o sacado, cedente
* o prazo para pagto e o valor.
*/
public interface Boleto {
String getSacado();
String getCedente();
Calendar getVencimento();
double getValor();

}

Passo 2
Uma vez que já definimos o que deve constar em um boleto para que ele possa ser gerado, agora vamos criar uma interface quer será capaz de criar o boleto. É essa interface que vai dizer aos bancos o que eles devem informar para ter o boleto, ou seja, é o nosso Builder:

package com.camilolopes.interfaces;

import java.util.Calendar;

/*
* interface para criação do Product
*/
public interface BoletoBuilder {
void buildSacado(String sacado);
void buildCedente(String cedente);
void buildValor(double valor);
void buildVencimento(Calendar vencimento);
void buildNossoNro(int nossoNro);

Boleto getBoleto();
}

Observe que agora definimos que na geração do boleto um número de controle é passado. Mas por que isso não está no Boleto? Simples, porque nesse caso não é obrigatório que o banco tenha um número de controle interno para gerar um boleto. Ele pode gerar N boletos para diferentes clientes e manter o número de controle (nossoNro) com o mesmo valor. Isso não impacta na regra de negócio de um boleto.

Passo 3
Uma vez que já definimos o que o boleto deve ter e como ele deve ser construído, agora vamos criar as classes (os bancos) que terão interesse em gerar um boleto.

ItauBoleto.java

package com.camilolopes.classes.impl;

import java.util.Calendar;

import com.camilolopes.interfaces.Boleto;
/*
* cada instituicao financeira monta o seu boleto
*/

public class ItauBoleto implements Boleto {

private String sacado;
private int nossoNro;
private Calendar vencimento;
private double valor;
private String cedente;

public ItauBoleto(String sacado, String cedente, double valor,
Calendar vencimento, int nossoNro) {
this.sacado =sacado;
this.cedente = cedente;
this.valor = valor;
this.vencimento = vencimento;
this.nossoNro = nossoNro;
}

@Override
public String getSacado() {
// TODO Auto-generated method stub
return sacado;
}

@Override
public String getCedente() {
// TODO Auto-generated method stub
return cedente;
}

@Override
public Calendar getVencimento() {
// TODO Auto-generated method stub
return vencimento;
}

@Override
public double getValor() {
// TODO Auto-generated method stub
return valor;
}

@Override
public String toString() {
return “ItauBoleto [sacado=” + sacado + “, nossoNro=” + nossoNro
+ “, vencimento=” + vencimento.getTime() + “, valor=” + valor
+ “, cedente=” + cedente + “]”;
}

}

Essa classe apenas implementa o Boleto informando e seus respectivos métodos. O método toString() é o mais importante, pois retornamos os dados do boleto quando obtemos o objeto dessa classe. Se eu quiser adicionar um logomarca ou informação extra no boleto é nesse momento que vamos implementar.

Note: infelizmente a data não está formatada comercialmente, mas não é o objetivo do post.

Passo 4
Agora eu já tenho o meu boleto para o Itaú, que é o ItauBoleto. Porém ele não é gerado ainda, preciso ter alguém que gere esse boleto. Ao consultar o sr. Builder ele falou que não pode fazer nada, apenas dizer o que eu preciso para gerar um boleto. Segundo sr. Builder, para gerar um boleto eu preciso dizer:o sacado, o cedente, valor, vencimento e um nro de controle.

ItauBoletoBuilder.java

package com.camilolopes.classes.impl;
import java.util.Calendar;
import com.camilolopes.interfaces.Boleto;
import com.camilolopes.interfaces.BoletoBuilder;

/*
* a instituicao tem um criador de boletos
*/
public class ItauBoletoBuilder implements BoletoBuilder {

private String sacado;
private String cedente;
private double valor;
private Calendar vencimento;
private int nossoNro;

@Override
public void buildSacado(String sacado) {
this.sacado = sacado;

}

@Override
public void buildCedente(String cedente) {
this.cedente = cedente;

}
@Override
public void buildValor(double valor) {
this.valor = valor;

}
@Override
public void buildVencimento(Calendar vencimento) {
this.vencimento = vencimento;

}
@Override
public void buildNossoNro(int nossoNro) {
this.nossoNro = nossoNro;
}
@Override
public Boleto getBoleto() {

return new ItauBoleto(sacado,cedente,valor,vencimento,nossoNro);
}
}

Agora sim já podemos obter um objeto Boleto pronto para uso. Uma vez que o ItauBoletoBuilder já pode construir um Boleto com base nas informações que ele recebe.

Passo 5
Porém a classe anterior apenas constrói um boleto para o ItauBoleto e o usuário não quer conhecer a construção de boleto, ele apenas quer passar um objeto de um dos bancos disponíveis  para que o boleto seja gerado. Para isso precisamos criar um GerenciadorDeBoletos que é capaz de gerar um boleto com base no tipo do objeto.

package com.camilolopes.generator;

import java.util.Calendar;

import com.camilolopes.interfaces.Boleto;
import com.camilolopes.interfaces.BoletoBuilder;
/*
* classe que gera o boleto a partir do xxBuilder
*/
public class GeneratorBoleto {
//código orientado a interfaces
private BoletoBuilder boletoBuilder;
//espero um objeto que implemente o criador de boleto
public GeneratorBoleto(BoletoBuilder boletoBuilder) {
super();
this.boletoBuilder = boletoBuilder;
}

public Boleto geraBoleto(){
//informações sobre o boleto a ser gerado
boletoBuilder.buildSacado(“Camilo Lopes”);
boletoBuilder.buildCedente(“Disney”);
boletoBuilder.buildValor(2000.00);

Calendar vencimento = Calendar.getInstance();
vencimento.add(Calendar.DATE, 20);
boletoBuilder.buildVencimento(vencimento);
boletoBuilder.buildNossoNro(1234);
//olha isso aqui que mágico
Boleto boleto = boletoBuilder.getBoleto();
return boleto;
}
}

E assim obtemos o objeto Boleto pronto para uso. Na classe Gerador de boletos não há nenhuma particularidade  de qual banco será gerado o boleto. Tudo isso depende do tipo do boleto recebido, ou seja, o que foi escolhido para ser gerado.

Passo 6
Podemos criar uma classe com o método main para visualizar o resultado. Vamos ver:

package com.camilolopes.main;

import com.camilolopes.classes.impl.ItauBoletoBuilder;
import com.camilolopes.generator.GeneratorBoleto;
import com.camilolopes.interfaces.Boleto;
import com.camilolopes.interfaces.BoletoBuilder;

public class GeradorBoletoMain {
public static void main(String[] args) {
BoletoBuilder boletoBuilder = new ItauBoletoBuilder();
GeneratorBoleto generatorBoleto = new GeneratorBoleto(boletoBuilder); //itau
Boleto boleto = generatorBoleto.geraBoleto();//retorna o boleto pronto
System.out.println(boleto);
}

}

O resultado será:

Resumindo…
Para finalizar, observe que com o padrão Builder definimos uma forma que os boletos podem ser gerados, ou seja, temos que implementar a interface Builder, é a única forma de construir um Boleto e para gerar temos uma classe responsável por qualquer tipo de Boleto, ou seja, ele gera boleto para qualquer classe que tem o boleto já construído, que nesse caso deve implementar BoletoBuilder (o nosso Builder). Adição de um novo banco é muito simples, o novo banco apenas precisa implementar a interface, nada no gerador é alterado. Observe o quanto isso é eficiente. Talvez se você não conhecesse o padrão Builder pensou em implementar usando if/else. Hmmm isso seria um inferno na sua vida, pois ia crescer/diminuir de acordo com a necessidade de negócio de ter mais ou menos bancos, sem falar que a legibilidade do código estaria comprometida.

Git Hub 

Os projetos encontram-se no Github:

https://camilolopes@github.com/camilolopes/workspacedesignpattern.git 

Vou ficando por aqui e espero que tenham gostado de mais um post da série DesignPattern não é receita de bolo.

Abraços, see ya!

2 comentários em “Série DesignPattern:Builder”

  1. Muito bom, da para tirar muitas duvidas.
    Na minha opinião,os comentários no decorrer do código ajudam muito no entendimento, continue comentado.

    Valeu mesmo…

Deixe um comentário para Charles Eduardo Cancelar resposta

O seu endereço de e-mail não será publicado.