Série DesignPattern:Bridge

Olá Pessoal,

No post de hoje vamos conhecer o padrão Bridge. Não é muito difícil surgir a necessidade do uso deste padrão. No exemplo de hoje você vai ver o motivo. Apesar de que você pode pensar: “hm, eu já implementei ele, mas nem fazia ideia”. Sim, isso é comum mesmo. Mas revise e veja se fez orientado à interfaces, pois é ai que está o pulo do gato.

Let’s go…

Bridge
A definição dele é bem curta veja:

“Separa a abstracao da representação de forma que ambos possam variar e produzir tipos de objetos diferentes”.

O que isso quer dizer?

Vamos ver de forma prática e com um exemplo do dia-dia. É comum em uma aplicação precisar gerar arquivos em formatos diferentes, certo? Se você desenvolve apenas focado nos formatos suportados atualmente terá um problema futuramente, caso surja a necessidade de suportar novos formatos. Vamos dizer que hoje sua aplicação gere arquivos apenas no formato .txt e .pdf., mas amanhã é necessário gerar arquivos no formato .doc e .xls. A forma que você implementa essa geração de arquivos vai dizer se isso custará muito ou não para o seu projeto. É preciso separar a abstração da representação (arquivo) com as formas que ele pode ser produzido. Independente do formato escolhido ele não deixa de ser um arquivo, o que varia ou muda é o tipo do arquivo. Captou a definição do Bridge?

Desenvolvimento
Vamos agora colocar mão na massa para ficar mais divertido e entender de fato o padrão Bridge. No projeto de hoje vamos poder gerar arquivos em diversos formatos. Nada de especial ai, porém tudo começa a ficar interessante se amanhã minha aplicação precisar suportar um formato especifico e ai que terei o “pulo do gato” ou não.

Passo 1
Crie um projeto conforme o meu a seguir:

Passo 2
Vamos pensar nos perguntando, certo?
O que eu quero?
R: Poder gerar um arquivo
Para quais formatos?
R: Os disponíveis na aplicação

Para a primeira pergunta vamos criar uma interface. O motivo é que nem tudo no sistema gera um arquivo, apenas em classes especificas de negócios fazem sentido gerar arquivos, como por exemplo:
Gerar um recibo para o cliente (classe recibo);
Gerar um arquivo com as despesas (classe despesas)
etc

Então as classes que precisam gerar um arquivo tem que implementar a interface, informar os dados que serão gerados e o tipo de arquivo desejado.

package br.com.camilolopes.bridge.interfaces;
/*
 * sempre que precisar gerar um arquivo
 * temos que implementar essa interface
 * e dizer o que teremos nesse arquivo
 */
public interface Arquivo {
    void geraArquivo();
}

Passo 2
Antes de definirmos quais classes da nossa aplicação são capazes de gerar arquivos, vamos criar o nosso gerador de arquivos, que na verdade será o Bridge na aplicação. Ele não passa de uma interface que será implementada pelos formatos suportados.

package br.com.camilolopes.bridge.interfaces;
/*
 * essa interface serão implementadas pelo
 * tipos de arquivos suportados a serem gerados
 * txt,pdf,zip,doc etc
 */
public interface GeradorDeArquivo {
    void criarArquivo(String dados);
}

Passo 3
Agora vamos dizer quais os formatos suportados. Aqui está o “pulo do gato”, se precisar adicionar um novo formato é só criar uma classe para o formato desejado e implementar a interface GeradorDeArquivo. Essa é a regra. E assim, todas as classes (recibo, desespas etc) podem gerar arquivos no novo formato disponível.

package br.com.camilolopes.bridge.classes;

import java.io.FileNotFoundException;
import java.io.PrintStream;

import br.com.camilolopes.bridge.interfaces.GeradorDeArquivo;
/*
 * classe que gera arquivo txt
 */
public class GeradorArquivoTxt implements GeradorDeArquivo {

    @Override
    public void criarArquivo(String dados) {
        try{
            PrintStream arquivo = new PrintStream(“arquivo.txt”);
            arquivo.println(dados);
            arquivo.close();
            System.out.println(“Arquivo gerado com sucesso”);
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }
}

Passo 4
Agora a classe que pode gerar arquivo (Recibo, Despesas etc). Nela delegamos a criação do arquivo para o Bridge e ele se encarrega de gerar o arquivo com o tipo que passamos.

package br.com.camilolopes.classes;

import br.com.camilolopes.bridge.interfaces.Arquivo;
import br.com.camilolopes.bridge.interfaces.GeradorDeArquivo;
/*
 * classe que tem os dados que serão
 * gerados os arquivos
 * ela delega a geração do arquivo para um especialista no assunto
 */
public class Recibo implements Arquivo {
    private String emissor;
    private String favorecido;
    private double valor;
    /*como sempre orientado a interfaces
     * assim deixamos a chamada com base no tipo
     * passado
     */
    private GeradorDeArquivo geradorDeArquivo;

    //para gerar o recibo o usuario é obrigado informar o tipo desejado
    public Recibo(String emissor, String favorecido, double valor,GeradorDeArquivo tipoDoArquivo) {
        super();
        this.emissor = emissor;
        this.favorecido = favorecido;
        this.valor = valor;
        geradorDeArquivo = tipoDoArquivo;
    }

    @Override
    public void geraArquivo()  {
        StringBuilder dados = new StringBuilder();
            dados.append(“Recibo: “);
            dados.append(“\n”);
            dados.append(“Empresa: ” + this.emissor);
            dados.append(“\n”);
            dados.append(“Cliente: ” + this.favorecido);
            dados.append(“\n”);
            dados.append(“Valor: “+ this.valor);
            //informando os dados que serão gerados
            this.geradorDeArquivo.criarArquivo(dados.toString());
    }
}

Observe que é obrigado ao usuário quando for gerar um arquivo, além das informações, é preciso dizer o tipo do arquivo que ele deseja.

Passo 5
Vamos testar a nossa aplicação. Criamos uma classe com o método main que informa os dados a serem gerados e o tipo de arquivo desejado e em seguida veremos se o arquivo foi gerado e se a informação foi gravada de fato.

package br.com.camilolopes.main;

import br.com.camilolopes.bridge.classes.GeradorArquivoTxt;
import br.com.camilolopes.classes.Recibo;

public class TesteRecibo {

    public static void main(String[] args) {
    GeradorArquivoTxt arquivoTxt = new GeradorArquivoTxt();
    Recibo reciboAbril = new Recibo(“XX”, “Camilo Lopes”, 50.00, arquivoTxt);
        reciboAbril.geraArquivo();
    }
}

Conclusão
Observe que seu código ficou muito melhor estruturado e com fácil manutenção.

Git Hub 

Os projetos encontram-se no Github:

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

Vou ficando por aqui e espero que tenham gostado do post de hoje.
Abraços, see ya!!

Série DesignPattern: Adapter

Olá Pessoal,
Mais um post da série “Design Pattern não é receita de bolo”. Hoje vamos conhecer o padrão Adapter. Pelo nome já temos ideia do que seria o padrão. Mas como seria à nível de código Java? Boa pergunta. Vamos ver isso no nosso post de hoje. Ah, o Adapter faz parte da família ‘Padrões Estruturais’.

Lets go…

Adapter

Substitui um objeto por outro que realiza a mesma tarefa, mas por uma interface diferente (não estamos  falando de interface Java aqui).

Exemplo:
As novas tomadas brasileiras realizam a mesma tarefa que as antigas, mas por uma interface diferente (com três pinos). Esse novo pino é uma nova funcionalidade.

Motivação
A motivação é querer adicionar uma nova funcionalidade ao sistema sem perder o comportamento e com custo baixo.No caso da tomada, o custo da mudança é comprar apenas um adaptador (adapter). Já pensou se além do adaptador tivéssemos que trocar o tipo do fio (somente para seguir o novo padrão)? Isso seria inviável e o custo muito alto. Um adaptador não pode gerar um custo alto para o novo modelo. Em aplicação, podemos pensar da seguinte forma: “adicionar aquele novo componente ao legado não pode custar caro demais e sim ter o custo considerável, como colocar mais memoria no servidor ou HD, mas não pode exigir um novo tipo de servidor de 1 milhão de dólares, por exemplo”. Quando estiver com dúvida com o Adapter, pergunte-se por que você tem adaptadores em sua casa? O motivo é que você não vai trocar toda sua instalação elétrica para o novo modelo.

Prática
A seguir temos uma prova de conceito da implementação do Adapter. Usei as novas tomadas brasileiras como exemplo. Novamente procure entender o uso do Adapter e como implementar. O código não tem nenhuma funcionalidade legal de ver, apenas um println com uma mensagem. O foco é conhecer o Design Pattern Adapter. A seguir uma modelagem bem didática, para facilitar o entendimento:

Passo 1
Como sempre, o nosso projeto:

Agora criamos a classe Tomada (ela possui a implementação antiga).

package br.com.camilolopes.classes;
/*
* tomada antiga
* ela não tem controle de energia como as novas
*/
public class Tomada {

public void on(){
System.out.println(“tomada conectada”);
}
public void off(){
System.out.println(“tomada desconectada”);
}
}

Passo 2
Obviamente que adapters são criados em classes já existentes. Então o primeiro ponto é ver onde precisa de um adaptador. No caso acima, precisamos de um adaptador nas tomadas. Para isso vamos extend a classe ‘Tomada’ já existente, assim fazemos tudo que ela já fez e não quebramos nada:

package br.com.camilolopes.adapters;

import br.com.camilolopes.classes.Tomada;
import br.com.camilolopes.newmodel.TomadaNova;
/*
* criando o adaptador
* ele que tem a responsabilidade de chamar o método novo
*/
public class TomadaAdapter extends Tomada {
private TomadaNova tomadaNova;

public TomadaAdapter() {
tomadaNova = new TomadaNova();
}

@Override
public void on() {
tomadaNova.on(true);
}

@Override
public void off() {
tomadaNova.off(false);
}

}

Acabamos de criar nosso Adapter.

E agora vamos criar a classe que tem a implementação das novas Tomadas.

TomadaNova.java

package br.com.camilolopes.newmodel;
/*
* aqui é a implementação do novo modelo de tomada
*/
public class TomadaNova {
public void on(boolean status){
System.out.println(“Novas tomadas Brs”);
checkEnergia(status);
}

private void checkEnergia(boolean status) {
if (status) {
System.out.println(“controle de segurança de energia está ” + status);
}else{
System.out.println(“controle de segurança de energia está ” + status);
}

}

public void off(boolean status) {
checkEnergia(status);

}
}

A classe Adapter é responsável por estar conectada à antiga tomada e com a nova. O usuário vai conectar ao Adapter e ele terá que chamar a nova implementação de tomada que, no nosso caso, eu disse que nas novas tomadas, há um sistema de controle de energia, por medida de segurança.

Criaremos a classe com o método main que será o ponto de entrada para receber a tomada e conectar ao adapter. Veja:

package br.com.camilolopes.main;

import br.com.camilolopes.adapters.TomadaAdapter;
import br.com.camilolopes.classes.Tomada;

public class TesteNovaTomada {

/**
* @param args
*/
public static void main(String[] args) {
/*
* conectando a tomada ao adaptador – adapter
*/
Tomada tomada = new TomadaAdapter();
//ligando a tomada
tomada.on();
//desligando a tomada
tomada.off();

}

}

O Resultado
Pronto! Veja que nosso Adapter já usa a versão da tomada.

 

Git Hub 

Os projetos encontram-se no Github:

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

Bom, é isso ai pessoal. Vou ficando por aqui. Espero que vocês estejam curtindo a série.
Abraços, see ya!!

Série DesignPattern: Singleton

Olá Pessoal,

Mais um post da série “Design Pattern: Não é receita de bolo”, e hoje vamos conhecer o Singleton. Esse será um post bem pequeno. O singleton é um padrão adorado por uns e criticado por outros. Como tudo na vida, há vantagens e desvantagens.Vamos conhecer um pouco sobre esse padrão hoje.
Lets go…

Singleton
O Singleton nos permite ter apenas uma única instância de uma classe. Ou seja, não podemos ter mais que um objeto de uma classe singleton ao mesmo tempo no sistema. E para garantir isso declaramos o construtor como private e temos um método static que controla a criação do objeto.

Um exemplo na prática. Vamos supor que temos uma classe responsável por ler dados de um arquivo de properties e esses dados serão usados dentro do sistema e não queremos ter mais que uma instância da classe para lê os dados do arquivo de properties.  Para isso, criamos um Singleton da classe que lê dados desse arquivo. A sintaxe para criar um Singleton é muitos simples, veja:

classe XX{
private static XX instancia;
private XX(){
}
public static XX getInstance(){
if(instancia==null){
XX.instancia = new Instancia();
}
return XX.instancia;
}

Sempre que precisarmos da instância dessa classe, apenas chamamos o método getInstance() e  teremos acesso aos métodos disponíveis. Vamos ver:

Desenvolvimento

package com.camilolopes.classes;
public class ReadConfiguration {
private static ReadConfiguration instance;
private ReadConfiguration(){
//ninguem pode instanciar essa classe
}
//metodo que controla a criação de objetos
public static ReadConfiguration getInstance(){
if (instance==null) {
ReadConfiguration.instance = new ReadConfiguration();
}

return ReadConfiguration.instance;
}
public String getUrl() {
// TODO Auto-generated method stub
return “url”;
}

}

Criaremos uma classe com o método main para testarmos:

package com.camilolopes.classes;

public class MainConfiguration {

public static void main(String[] args) {
ReadConfiguration configuration = ReadConfiguration.getInstance();
System.out.println(configuration.getUrl());
}
}

Git Hub 

Os projetos encontram-se no Github:

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

Simples não? Vou ficando por aqui e espero que tenham gostado do post.

Abracos, see ya!!

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