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
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()
.