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 alguns conceitos de herança.
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 https://www.mballem.com/ */ public interface Artigo { void setAutor(String nome); void setTitulo(String nome); void setMarcacoes(Listmarcacoes); 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 https://www.mballem.com/ */ public class Blog implements Artigo { private String autor; private String titulo; private Listmarcacoes; 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 https://www.mballem.com/ */ public class Revista implements Artigo { private String autor; private String titulo; private Listmarcacoes; 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 https://www.mballem.com/ */ public class Jornal implements Artigo { private String autor; private String titulo; private Listmarcacoes; 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 https://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 https://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 https://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 https://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 https://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 = JornalBuilder.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()
.