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!

Série DesignPattern: Factory Method

Olá Pessoal,

Dando continuidade a série “Design Pattern não é receita de bolo” hoje vamos conhecer o padrão Factory Method que pertence aos Padrões de Criação. Para quem não acompanhou o ultimo post, conhecemos como Design Pattern é dividido, saber qual padrão usar é tão importante quanto saber que tipo de padrão ele pertence.

Lets go…

Download do Projeto

Os projetos que serão usados no post estão no meu GitHub:

https://github.com/camilolopes/workspacedesignpattern

note: Sei que para alguns nem merecia colocar esse note por ser obvio, mas por medida de segurança achei bom não faltar. Esqueça tradução ao querer estudar Design Pattern. É em inglês o nome do padrão e evite ao máximo de traduzir. O motivo, que a tradução atrapalha mais do que ajuda.

Factory Method (Padrão de Criação)

Encapsula a escolha da classe concreta na criação de objetos de um TIPO.Ou seja, é quando tenho várias “respostas” com base no TIPO passado.

Exemplo:
se o cliente é JURIDICO(do tipo juridico) envio uma mensagem com o CNPJ;
se o cliente é FISICO(do tipo fisico) envio  uma mensagem com CPF;

Esse padrão normalmente tem um intermediario que ajuda decidir qual implementacao da classe concreta será chamada com base no tipo da chamada.

Esqueleto do padrão

  • Product: é interface que define o objeto a ser criado
  • ConcreteProduct: implementação particular do tipo do objeto a ser criado
  • Creator: classe/interface que define a assinatura do metodo responsavel pela criacao do produto
  • concreteCreator: classe que implementa ou sobrescreve o método de criação do produto.

Na prática

Veremos agora na pratica ele em ação, crie um projeto seu Eclipse e chame como achar melhor, aqui chamei de  FactoryMethodPessoa com essa estrutura:

Deixe seu projeto conforme a imagem acima. A seguir vamos meter mão na massa.

Desenvolvendo
Chegou a hora de desenvolver. O ponto mais importante ao querer entender como usar o design pattern X ou Y. É entender o objetivo dele. Na maioria das vezes, um design pattern sempre começa por uma interface para manter o código orientado a interfaces. Mas, isso não é regra. Vou fazer um desenvolvimento orientado a questionamento e alinhamento como tipo de design pattern. Foi assim que fiz para buscar entender e saber usar na hora “H”. Eu particularmente não tive ainda a felicidade de usar todos os design pattern em uma “solução cliente”, como prova de conceito é sempre mais simples. Mas, em regra de negócio as coisas são mais dinâmicas. Vamos largar de papo e mão na massa.

Passo 1
Eu me pergunto: o que minha aplicação pode fazer?  Enviar mensagens para diferentes tipos de pessoa.
Então é bom eu ter uma interface onde cada pessoa diz como quer ser chamada.

package br.com.camilolopes.interfaces;

/* Product */
public interface Envio {
void enviar(String mensagem);
}

Posso dizer que o serviço de envio de mensagens é um produto? Sim. E que será implementado com base no tipo de mensagem que deseja enviar.

Passo 2
Agora precisamos definir que tipo de mensagens vamos poder enviar. No nosso exemplo, podemos mandar mensagens com o CPF ou CNPJ, porém isso depende do tipo do cliente. Não posso mandar CPF para um cliente que é pessoa Juridica. A seguir temos a implementação da maneira mais simples, possível o objetivo é entender o design pattern.

PessoaFisica.java

package br.com.camilolopes.classes.impl;
import br.com.camilolopes.interfaces.Envio;
/*
* concreteProduct
*/
public class PessoaFisica implements Envio {
@Override
public void enviar(String mensagem) {
System.out.println(mensagem + “CPF “);
}}

PessoaJuridica.java

package br.com.camilolopes.classes.impl;
import br.com.camilolopes.interfaces.Envio;
/*
* ConcreteProduct
*/
public class PessoaJuridica implements Envio {
@Override
public void enviar(String mensagem) {
System.out.println(mensagem + “CNPJ”);
}}

Observe que para cada PessoaXX passamos uma mensagem  + uma informação privada que cada um tem. Que nesse caso seria o CPF e CNPJ. Pense assim à nivel de negócio:
“Se eu estou me comunicando com Pessoa Juridica as minhas negociações, contrato serão feitas em cima de um CNPJ, é ele que é importante na relação.” Então nesse contexto se eu quiser mandar uma  mensagem e no final envio o cnpj para confirmação.

Exemplo:
“Houve uma alteração de contrato para o CNPJ xxx”

Passo 3
Já temos as implementações do produto. Agora que vem a parter do Factory Method, onde precisamos chamar cada implementação com base no tipo. Algo assim:

Se for PessoaFisica
    chame new PessoaFisica().enviar
senão se for PessoaJuridica
    chame new PessoaJuridica().enviar

ifs encadeados nunca é uma boa opção, afeta a legibilidade e manutenção do código. Sem falar que amanhã pode nascer um novo tipo de PessoaXXX e teremos que aumentar mais ainda essa condição if/else. E veremos a seguir que conseguirmos evitar isso através do padrão factory method.

Criaremos uma classe que retorna o tipo do objeto com base no que foi passado para ele. E com esse retorno, chamaremos uma das implementações da nossa interface (Product). Veja:

package br.com.camilolopes.creator;
import br.com.camilolopes.interfaces.Envio;
/*
* classe que vai retornar o metodo implementado
* com base no tipo que recebeu
*/
public class CreatorEnvio {
//o segredo que evita N if/else encadeados \o/
public Envio getEnvio(Envio envio){
if (envio==null) {
throw new NullPointerException();
}
return envio;
}
}

Observe o segredo todo está aqui. Estamos com o código orientado à interfaces. O nosso método espera qualquer objeto que seja do tipo Envio, ou seja, qualquer implementação da interface. Verificamos se é null para não acontecer NullPointerException ao chamar o método e em seguida retornamos o objeto que recebemos.
E agora?
Simples. A classe principal que envia as mensagens, chamará CreatorEnvio passando para o tipo de pessoa que ele deseja enviar a mensagem e que mensagem será enviada. Veja:

package br.com.camilolopes.main;
import br.com.camilolopes.classes.impl.PessoaFisica;
import br.com.camilolopes.classes.impl.PessoaJuridica;
import br.com.camilolopes.creator.CreatorEnvio;
import br.com.camilolopes.interfaces.Envio;

public class MainPessoa {

public static void main(String[] args) {
CreatorEnvio creator = new CreatorEnvio();

//enviando mensagem para pessoa fisica
Envio envio = creator.getEnvio(new PessoaFisica());
envio.enviar(“caro cliente inscrito no “);

//enviando para pessoa juridica
envio = creator.getEnvio(new PessoaJuridica());
envio.enviar(“Prezada Empresa inscrita no “);
}
}

Observe que é muito simples, todo código orientado à interfaces. Se amanhã nascer um novo tipo de Pessoa, não precisamos nos preocupar, apenas passaremos isso para o método getEnvio e ele chamará a implementação de enviar(mensagem) daquele novo tipo de PessoaXX.

O resultado

Eu gosto de criar unit tests, a seguir tem o unit test que fiz para validar. Com unit test não precisamos ter uma classe com o método main para saber se o nosso código está funcionando. Veja:

package br.com.camilolopes.creator;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import br.com.camilolopes.classes.impl.PessoaFisica;
import br.com.camilolopes.classes.impl.PessoaJuridica;
import br.com.camilolopes.creator.CreatorEnvio;

public class CreatorEnvioTest {

private CreatorEnvio creatorEnvio;

@Before
public void setUp() throws Exception {
creatorEnvio = new CreatorEnvio();
}

@Test
public void testGetPessoaFisica() {
assertTrue(creatorEnvio.getEnvio(new PessoaFisica()) instanceof PessoaFisica);
}

@Test
public void testGetPessoaJuridica() {
assertTrue(creatorEnvio.getEnvio(new PessoaJuridica()) instanceof PessoaJuridica);
}

@Test(expected = NullPointerException.class)
public void testNullIsInvalid() {
creatorEnvio.getEnvio(null);
}
}

Criei apenas um unit test para a classe CreatorEnvio e verifiquei se ele estava retornando o tipo do  objeto correto com base no que foi passado para o método getEnvio.

Git Hub 

Os projetos encontram-se no Github:

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

Vou ficando por aqui espero que tenham gostado do post. Apesar de ter ficado grande devido a parte pratica hehe.

Abracos, see ya