Simplificando com Builder Pattern – 2

No tutorial anterior, Simplificando com Builder Pattern, vimos uma abordagem de como utilizar o padrão de projeto Builder na construção de um objeto do tipo Pessoa que continha ainda atributos dos tipos Telefone e Endereco acoplados. Desta fez, vamos utilizar o padrão Builder de uma forma um pouco mais avançada, onde usaremos contratos por implementação de interfaces.

1. Analisando o projeto

Vamos considerar que uma editora possui os seguintes meios de publicações que são: revista, jornal e blog. Sempre que um autor escrever um artigo, ele deverá adicionar este artigo e suas informações adicionais a um destes meios de comunicação. Para isso, vamos ter uma interface Java chamada Artigo e três classes que a implementa que são: Blog, Revista e Jornal.

Figura 1

Figura 1

A interface Artigo define a obrigatoriedade das classes concretas Blog, Revista e Jornal implementar os métodos do tipo set() e também o método toString(). Veja na Listagem 1 o código fonte da interface Artigo:

Listagem 1. Interface Artigo.

package com.mballem.pattern.domain;

import java.util.Date;
import java.util.List;

/**
 * Created by http://www.mballem.com/
 */
public interface Artigo {

    void setAutor(String nome);
    void setTitulo(String nome);
    void setMarcacoes(List marcacoes);
    void setPaginas(int paginas);
    void setDataDaPublicacao(Date data);
    void setTexto(String texto);
    String toString();
}

Na Listagem 2 temos a classe concreta Blog implementando a interface Artigo:

Listagem 2. Classe Blog.

package com.mballem.pattern.domain;

import java.util.Date;
import java.util.List;

/**
 * Created by http://www.mballem.com/
 */
public class Blog implements Artigo {

    private String autor;
    private String titulo;
    private List marcacoes;
    private int paginas;
    private Date publicado;
    private String texto;

    //Gere também os métodos getters de cada atributo.

    @Override
    public void setAutor(String autor) {
        this.autor = autor;
    }

    @Override
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    @Override
    public void setMarcacoes(List marcacoes) {
        this.marcacoes = marcacoes;
    }
    
    @Override
    public void setPaginas(int paginas) {
        this.paginas = paginas;
    }

    @Override
    public void setDataDaPublicacao(Date data) {
        this.publicado = data;
    }

    @Override
    public void setTexto(String texto) {
        this.texto = texto;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "autor='" + autor + '\'' +
                ", titulo='" + titulo + '\'' +
                ", marcacoes=" + marcacoes +
                ", paginas=" + paginas +
                ", publicado=" + publicado +
                ", texto='" + texto + '\'' +
                '}';
    }
}

Veja agora – Listagem 3 – a classe Revista:

Listagem 3. Classe Revista.

package com.mballem.pattern.domain;

import java.util.Date;
import java.util.List;

/**
 * Created by http://www.mballem.com/
 */
public class Revista implements Artigo {

    private String autor;
    private String titulo;
    private List marcacoes;
    private int paginas;
    private Date publicado;
    private String texto;

    //Gerar os métodos getters para cada atributo. 

    @Override
    public void setAutor(String autor) {
        this.autor = autor;
    }

    @Override
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    @Override
    public void setMarcacoes(List marcacoes) {
        this.marcacoes = marcacoes;
    }

    @Override
    public void setPaginas(int paginas) {
        this.paginas = paginas;
    }

    @Override
    public void setDataDaPublicacao(Date data) {
        this.publicado = data;
    }

    @Override
    public void setTexto(String texto) {
        this.texto = texto;
    }

    @Override
    public String toString() {
        return "Revista{" +
                "autor='" + autor + '\'' +
                ", titulo='" + titulo + '\'' +
                ", marcacoes=" + marcacoes +
                ", paginas=" + paginas +
                ", publicado=" + publicado +
                ", texto='" + texto + '\'' +
                '}';
    }
} 

Por fim, adicione ao projeto a classe Jornal, conforme Listagem 4:

Listagem 4. Classe Jornal.

package com.mballem.pattern.domain;

import java.util.Date;
import java.util.List;

/**
 * Created by http://www.mballem.com/
 */
public class Jornal implements Artigo {

    private String autor;
    private String titulo;
    private List marcacoes;
    private int paginas;
    private Date publicado;
    private String texto;

    //Gerar os métodos getters para cada atributo.

    @Override
    public void setAutor(String autor) {
        this.autor = autor;
    }

    @Override
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    @Override
    public void setMarcacoes(List marcacoes) {
        this.marcacoes = marcacoes;
    }

    @Override
    public void setPaginas(int paginas) {
        this.paginas = paginas;
    }

    @Override
    public void setDataDaPublicacao(Date data) {
        this.publicado = data;
    }

    @Override
    public void setTexto(String texto) {
        this.texto = texto;
    }

    @Override
    public String toString() {
        return "Jornal{" +
                "autor='" + autor + '\'' +
                ", titulo='" + titulo + '\'' +
                ", marcacoes=" + marcacoes +
                ", paginas=" + paginas +
                ", publicado=" + publicado +
                ", texto='" + texto + '\'' +
                '}';
    }
}

2. Construindo o Builder

Vamos agora começar a criar as classes que representarão o padrão builder neste projeto. O primeiro passo será definir uma interface, a qual será nomeada de ArtigoBuilder, conforme Listagem 5. Note que ArtigoBuilder define os métodos autor(), titulo(), assuntos(), totalDePaginas(), publicadoEm() e texto(), os quais retornam um objeto ArtigoBuilder.

Outro método importante na interface é o get(). Este método tem como retorno um objeto Artigo, o qual retorna o objeto criado pelo builder, que será um Blog, Revista ou Jornal.

Listagem 5. Interface ArtigoBuilder.

package com.mballem.pattern.builder;

import com.mballem.pattern.domain.Artigo;

/**
 * Created by http://www.mballem.com/
 */
public interface ArtigoBuilder {

    ArtigoBuilder autor(String autor);
    ArtigoBuilder titulo(String titulo);
    ArtigoBuilder assuntos(String... assuntos);
    ArtigoBuilder totalDePaginas(int total);
    ArtigoBuilder publicadoEm(int dia, int mes, int ano);
    ArtigoBuilder texto(String texto);

    Artigo get();
}

Veja a seguir as classes concretas BlogBuilder, RevistaBuilder e JornalBuilder. Estas classes vão implementar os métodos da interface ArtigoBuilder. Deste modo, se o autor desejar cadastrar um artigo em um blog, usará a classe BlogBuilder, RevistaBuilder para uma revista e JornalBuilder para adicionar um artigo a um jornal.

Na Listagem 6, temos o código fonte da classe BlogBuilder. A classe recebe um atributo de instancia do tipo Artigo, o qual fornece o acesso aos métodos da interface Artigo, implementados pela classe Blog. No método construtor de BlogBuilder, inicializamos o atributo artigo com uma instancia de Blog. O método estático builder() criar uma instancia para a classe BlogBuilder, retornando-a como um objeto do tipo ArtigoBuilder. E o método get() vai retornar a instancia atual do atributo artigo.

Listagem 6. Classe BlogBuilder.

package com.mballem.pattern.builder;

import com.mballem.pattern.domain.Artigo;
import com.mballem.pattern.domain.Blog;

import java.util.Arrays;
import java.util.Calendar;

/**
 * Created by http://www.mballem.com/
 */
public class BlogBuilder implements ArtigoBuilder {

    private Artigo artigo;

    public BlogBuilder() {
        this.artigo = new Blog();
    }

    public static ArtigoBuilder builder() {
        return new BlogBuilder();
    }

    @Override
    public ArtigoBuilder autor(String autor) {
        this.artigo.setAutor(autor);
        return this;
    }

    @Override
    public ArtigoBuilder titulo(String titulo) {
        this.artigo.setTitulo(titulo);
        return this;
    }

    @Override
    public ArtigoBuilder assuntos(String... assuntos) {
        this.artigo.setMarcacoes(Arrays.asList(assuntos));
        return this;
    }

    @Override
    public ArtigoBuilder totalDePaginas(int total) {
        this.artigo.setPaginas(total);
        return this;
    }

    @Override
    public ArtigoBuilder publicadoEm(int dia, int mes, int ano) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(ano, mes, dia);
        this.artigo.setDataDaPublicacao(calendar.getTime());
        return this;
    }

    @Override
    public ArtigoBuilder texto(String texto) {
        this.artigo.setTexto(texto);
        return this;
    }

    @Override
    public Artigo get() {
        return this.artigo;
    }
}    

Os demais métodos são as implementações dos métodos definidos pela interface ArtigoBuilder. A seguir, temos a classe concreta RevistaBuilder, conforme a Listagem 7.

Listagem 7. Classe RevistaBuilder.

package com.mballem.pattern.builder;

import com.mballem.pattern.domain.Artigo;
import com.mballem.pattern.domain.Revista;

import java.util.Arrays;
import java.util.Calendar;

/**
 * Created by http://www.mballem.com/
 */
public class RevistaBuilder implements ArtigoBuilder {

    private Artigo artigo;

    public RevistaBuilder() {
        this.artigo = new Revista();
    }

    public static ArtigoBuilder builder() {
        return new RevistaBuilder();
    }

    @Override
    public ArtigoBuilder autor(String autor) {
        this.artigo.setAutor(autor);
        return this;
    }

    @Override
    public ArtigoBuilder titulo(String titulo) {
        this.artigo.setTitulo(titulo);
        return this;
    }

    @Override
    public ArtigoBuilder assuntos(String... assuntos) {
        this.artigo.setMarcacoes(Arrays.asList(assuntos));
        return this;
    }

    @Override
    public ArtigoBuilder totalDePaginas(int total) {
        this.artigo.setPaginas(total);
        return this;
    }

    @Override
    public ArtigoBuilder publicadoEm(int dia, int mes, int ano) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(ano, mes, dia);
        this.artigo.setDataDaPublicacao(calendar.getTime());
        return this;
    }

    @Override
    public ArtigoBuilder texto(String texto) {
        this.artigo.setTexto(texto);
        return this;
    }

    @Override
    public Artigo get() {
        return this.artigo;
    }
}    

Para finalizar as classes que envolvem o padrão Builder, temos na Listagem 8 a classe concreta JornalBuilder.

Listagem 8. Classe JornalBuilder.

package com.mballem.pattern.builder;

import com.mballem.pattern.domain.Artigo;
import com.mballem.pattern.domain.Jornal;

import java.util.Arrays;
import java.util.Calendar;

/**
 * Created by http://www.mballem.com/
 */
public class JornalBuilder implements ArtigoBuilder {

    private Artigo artigo;

    public JornalBuilder() {
        this.artigo = new Jornal();
    }

    public static ArtigoBuilder builder() {
        return new JornalBuilder();
    }

    @Override
    public ArtigoBuilder autor(String autor) {
        this.artigo.setAutor(autor);
        return this;
    }

    @Override
    public ArtigoBuilder titulo(String titulo) {
        this.artigo.setTitulo(titulo);
        return this;
    }

    @Override
    public ArtigoBuilder assuntos(String... assuntos) {
        this.artigo.setMarcacoes(Arrays.asList(assuntos));
        return this;
    }

    @Override
    public ArtigoBuilder totalDePaginas(int total) {
        this.artigo.setPaginas(total);
        return this;
    }

    @Override
    public ArtigoBuilder publicadoEm(int dia, int mes, int ano) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(ano, mes, dia);
        this.artigo.setDataDaPublicacao(calendar.getTime());
        return this;
    }

    @Override
    public ArtigoBuilder texto(String texto) {
        this.artigo.setTexto(texto);
        return this;
    }

    @Override
    public Artigo get() {
        return this.artigo;
    }
}    

3. Usando o Builder Pattern

Na classe CadastraArtigo você poderá ver um exemplo de como criar os objetos para Revista, Blog e Jornal usando as classes criadas com base no padrão Builder. Veja na Listagem 9:

Listagem 9. Classe CadastraArtigo.

package com.mballem.pattern;

import com.mballem.pattern.builder.BlogBuilder;
import com.mballem.pattern.builder.JornalBuilder;
import com.mballem.pattern.domain.Artigo;

/**
 * Created by http://www.mballem.com/
 */
public class CadastraArtigo {

     public static void main(String[] args) {
        Artigo blog = BlogBuilder.builder()
                .autor("Marcio Ballem")
                .titulo("BlogBuilder")
                .assuntos("builder pattern", "java")
                .publicadoEm(20, 3, 2005)
                .texto("O padrão de projeto builder...")
                .get();

        System.out.println(blog.toString());

        Artigo jornal = JornalBuilder.builder()
                .autor("Marcio Ballem")
                .titulo("JornalBuilder")
                .assuntos("builder pattern", "java")
                .publicadoEm(25, 5, 2008)
                .texto("O padrão de projeto builder...")
                .totalDePaginas(1)
                .get();

        System.out.println(jornal.toString());

        Artigo revista = RevistaBuilder.builder()
                .autor("Marcio Ballem")
                .titulo("RevistaBuilder")
                .assuntos("builder pattern", "java")
                .publicadoEm(25, 5, 2008)
                .texto("O padrão de projeto builder...")
                .totalDePaginas(6)
                .get();

        System.out.println(revista.toString());
    }
}

Note que para criar um objeto do tipo Blog o builder usado foi o BlogBuilder. A instancia de BlogBuilder é criada a partir da chamada ao método estático builder(). E os método como autor(), titulo(), assuntos(), entre outros, são utilizados para adicionar os valores desejados ao objeto Blog que será retornado pelo método get() de BlogBuilder. Após atribuir o retorno de get() a variável blog, o método toString() exibe o conteúdo cadastrado.

O mesmo processo é realizado a seguir para cadastro de um objeto Revista e Jornal. Modificando apenas o builder referente a cada objeto que se deseja retornar.

Se você desejar retornar os valores cadastrados a partir dos métodos get(), como getAutor(), verá que no exemplo da Listagem 9 não será possível. Isto porque, os métodos do tipo get() não foram definidos na interface Artigo.

Então, se desejar, defina estes métodos na interface Artigo. Ou você pode, ao invés de criar variáveis do tipo Artigo, no método main() de CadastraArtigo, crie a variável com o tipo concreto, e fazer um cast para o retornar um objeto deste tipo, como no exemplo a seguir:

public class CadastraArtigo {

    public static void main(String[] args) {
        Blog blog = (Blog) BlogBuilder.builder()
                .autor("Marcio Ballem")
                .titulo("BlogBuilder")
                .assuntos("builder pattern", "java")
                .publicadoEm(20, 3, 2005)
                .texto("O padrão de projeto builder...")
                .get();

        System.out.println(blog.getAutor());
        System.out.println(blog.getTitulo());
        System.out.println(blog.getTexto());
    }
}

O ideal é definir os métodos get() na interface Artigo. Isto não foi realizado no tutorial apenas para reduzir um pouco a quantidade de código fonte, por isso foi definido o método toString().

Ballem

Marcio Ballem é bacharel em Sistemas de Informação pelo Centro Universitário Franciscano em Santa Maria/RS. Tem experiência com desenvolvimento Delphi e Java em projetos para gestão pública e acadêmica. Possui certificação em Java, OCJP 6.

Você pode gostar...