Logo Passei Direto
Buscar

Padrões de Projeto de Software com Java

Ferramentas de estudo

Questões resolvidas

Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Questões resolvidas

Prévia do material em texto

Padrões GOF de criação
Você vai estudar o processo tradicional de criação de objetos, que envolve a chamada de um método
construtor com um operador de alocação de memória, resultando em código acoplado e rígido. O
entendimento dos padrões de criação permite gerenciar objetos de forma mais flexível, facilitando a
evolução dos sistemas dentro de prazos e custos viáveis. Entre esses padrões, destacam-se: Factory
Method, Abstract Factory, Builder, Prototype e Singleton.
Professores Alexandre Luis Correa e Denis Cople
1. Itens iniciais
Objetivos
Reconhecer o propósito, a estrutura e as situações de aplicação do padrão de projeto Factory Method.
 
Reconhecer o propósito, a estrutura e as situações de aplicação do padrão de projeto Abstract Factory.
 
Reconhecer o propósito, a estrutura e as situações de aplicação do padrão de projeto Builder.
 
Reconhecer o propósito, a estrutura e as situações de aplicação dos padrões de projeto Prototype e
Singleton.
Introdução
Os padrões GOF de criação auxiliam na construção de sistemas independentes da forma com que os objetos
são criados e representados. Neste vídeo, você vai descobrir como aplicar esses padrões em projetos
orientados a objetos. Não perca!
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
• 
• 
• 
• 
1. Padrão de projeto Factory Method
Intenção e problema do padrão Factory Method
Em diversas situações, podem ser definidos processos capazes de trabalhar com diferentes tipos de objetos,
mas a forma tradicional para instanciá-los exige a especificação da classe no código, inviabilizando a
reutilização. Muito utilizado em frameworks, os quais visam naturalmente o reúso de processos, o padrão
Factory Method define uma interface para a criação de objetos, mas permite que as subclasses definam o tipo
de objeto específico que será gerado, ou seja, delega para os descendentes a lógica de criação, diminuindo o
acoplamento do código e viabilizando o reúso dos processos em contextos diferenciados.
 
Entenda no vídeo a seguir a intenção e o problema do padrão Factory Method. Vamos lá!
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Factory Method é um padrão frequentemente utilizado na implementação de frameworks. Ele define uma
interface para a criação de objetos, deixando para as subclasses a decisão sobre a classe específica a ser
instanciada.
Você conhece o problema que o padrão Factory Method busca resolver?
Suponha que a sua tarefa seja implementar um método que remova os itens inválidos de uma coleção de itens
de um pedido. Um item de pedido possui os seguintes atributos:
 
Quantidade
 
Preço unitário
 
Produto solicitado
 
Imagine que um item válido é aquele que tenha uma quantidade de 1 a 100. Veja a implementação da classe 
ItemPedido a seguir.
 
Além dos atributos, das operações de acesso e do construtor, essa classe define a operação valor, que
retorna o valor do item resultante da multiplicação da quantidade pelo preço unitário.
• 
• 
• 
java
public class ItemPedido {
 private Produto produto;
 private int quantidade;
 private int precoUnitarioEmCentavos;
 public ItemPedido(Produto produto, int quantidade, int valorEmCentavos) {
 this.produto = produto;
 this.quantidade = quantidade;
 this.precoUnitarioEmCentavos = valorEmCentavos;
 }
 public int valor() {
 return quantidade * precoUnitarioEmCentavos;
 }
 public Produto getProduto() {
 return produto;
 }
 public void setProduto(Produto produto) {
 this.produto = produto;
 }
 public int getQuantidade() {
 return quantidade;
 }
 public void setQuantidade(int quantidade) {
 this.quantidade = quantidade;
 }
 public int getPrecoUnitarioEmCentavos() {
 return precoUnitarioEmCentavos;
 }
 public void setPrecoUnitarioEmCentavos(int precoUnitarioEmCentavos) {
 this.precoUnitarioEmCentavos = precoUnitarioEmCentavos;
 }
} 
Uma primeira implementação da operação de remoção dos itens inválidos está listada a seguir.
java
public void removerItensInvalidos(ArrayList‹ItemPedido› itens) {
 ArrayIterator‹ItemPedido› cursor = new ArrayIterator(itens);
 while (cursor.hasNext()) {
 ItemPedido item = cursor.next();
 if (! isValido(item)) {
 cursor.remove(item);
 }
 }
}
public boolean isValido(ItemPedido item) {
 return (item.getQuantidade() › 0 && item.getQuantidade() ‹ 100);
} 
Nessa implementação, imagine que definimos uma classe ArrayIterator que implementa um cursor sobre os
itens de pedido recebidos como parâmetro com as seguintes operações:
hasNext
Verifica se existe um próximo elemento no ArrayList ou se o cursor já está posicionado no último
elemento.
next
Retorna o próximo elemento do ArrayList. Na primeira chamada, ele retorna o primeiro elemento da
coleção.
remove
Remove um elemento da coleção.
Você consegue identificar o principal problema dessa solução?
 
Embora funcione, essa solução utiliza a classe ArrayList, criando um acoplamento da implementação com uma
forma específica de organização dos itens.
 
Suponha que os pedidos passem a ser organizados em um HashSet, por exemplo. O efeito negativo desse
acoplamento fica evidente, pois teremos de modificar a implementação, uma vez que a forma de percurso em
um HashSet é diferente daquela utilizada em um ArrayList.
 
Poderíamos desenvolver outra versão específica para um HashSet, definindo uma classe HashSetIterator e
implementando as mesmas operações da classe ArrayListIterator, mas com um algoritmo específico para o
percurso e a manipulação dos elementos.
 
O código a seguir apresenta a versão da operação removerItensInvalidos implementada a partir de um
HashSet.
java
public void removerItensInvalidos(HashSet‹ ItemPedido› itens) {
 HashSetIterator‹ItemPedido› cursor = new HashSetIterator(itens);
 while (cursor.hasNext()) {
 ItemPedido item = cursor.next();
 if (! isValido(item)) {
 cursor.remove(item);
 }
 }
} 
E se houvesse diversos outros tipos de coleção. Você faria uma nova implementação para cada tipo específico
de coleção?
Indo além, imagine que esse problema que você
enfrentou em uma operação específica do
sistema (isto é, remover itens de pedido
inválidos) ocorra em dezenas de outras
situações do mesmo sistema. O resultado será
uma enorme replicação de código, que é um
dos principais inimigos da evolução sustentável
de um sistema.
 
Note que as duas implementações
apresentadas são muito parecidas, diferindo
apenas pelo tipo de coleção e do cursor criado.
Considerando que todas as coleções
implementam um tipo específico Collection, uma alternativa seria definir uma única operação
removerItensInvalidos e instanciar o cursor específico para a coleção recebida como parâmetro.
 
O código a seguir apresenta essa implementação alternativa. Confira!
java
public void removerItensInvalidos(Collection‹ItemPedido› itens) throws Exception {
 Iterator‹ItemPedido› cursor = null;
 if (itens instanceof ArrayList)
 cursor = new ArrayIterator((ArrayList) itens);
 else if (itens instanceof HashSet)
 cursor = new HashSetIterator((HashSet) itens);
 if (cursor == null)
 throw new Exception("tipo da coleção de itens inválido");
 while (cursor.hasNext()) {
 ItemPedido item = cursor.next();
 if (! isValido(item)) {
 cursor.remove(item);
 }
 }
} 
Com essa solução, implementamos apenas uma operação removerItensInvalidos capaz de operar com um
ArrayList ou com um HashSet. Agora, imagine que existissem vários outros tipos de coleção.
 
Você consegue visualizar a enorme quantidade de comandos condicionais que deveriam ser adicionados?
Portanto, esse código teria de ser modificado a
cada novo tipo de implementação de coleção,
acumulando uma quantidade significativa de
expressões condicionais, o que é uma violação
clara do princípio Open Closed – um dos
princípios SOLID.
 
Além disso, esse código apresenta estruturas
baseadas em downcasting, o que é um
indicativo de deficiência na estruturade famílias de produtos relacionados, de forma que o módulo 
cliente não dependa das classes específicas que representam os produtos de 
determinada família.
 
 C 
Garantir que uma classe tenha apenas uma instância em um processo de execução, 
permitindo apenas que ela própria possa criar e fornecer acesso a essa instância.
 
 D 
Permitir que um módulo não precise criar diretamente instâncias de subclasses de uma 
hierarquia de produtos, fornecendo uma operação genérica de criação e delegando a 
instanciação dos produtos específicos para cada subclasse que implemente essa operação 
genérica.
 
 E 
Permitir a definição de um processo de construção capaz de instanciar 
diferentes representações de um objeto complexo.
 
A alternativa A está correta.
As alternativas de B a E correspondem, respectivamente, às intenções dos padrões Abstract Factory, 
Singleton, Factory Method e Builder.
Solução do padrão Prototype
O padrão Prototype propõe uma solução simples: um método de clonagem na classe, responsável por 
instanciar um novo objeto e replicar seu estado atual. No entanto, é preciso atenção com ponteiros, como em 
objetos Java, para evitar compartilhamento indevido de memória. Por exemplo, se um atributo Date for 
copiado diretamente, ele não criará uma nova instância, mas compartilhará a existente. Assim, qualquer 
alteração no objeto clonado também modificará o original.
 
Compreenda no vídeo a seguir a solução do padrão Prototype.
 
 
 
 
 
 
 
Conteúdo interativo
 
Acesse a versão digital para assistir ao vídeo.
 
 
 
 
O diagrama a seguir ilustra a estrutura da solução proposta pelo padrão Prototype.
Estrutura geral da solução proposta pelo padrão Prototype.
A ideia central do padrão é fazer com que uma classe cliente que precise criar instâncias de uma subclasse 
específica ou de diferentes subclasses registre uma instância protótipo dessa(s) subclasse(s) e chame a 
operação clone do protótipo registrado sempre que precisar de uma nova instância.
 
A operação clone é definida em cada subclasse e retorna para o módulo cliente uma nova instância com uma 
réplica de seu estado. Dessa forma, o módulo cliente não sabe qual subclasse específica foi instanciada, e 
novas subclasses podem ser adicionadas ao esquema, sem que o cliente precise ser modificado.
 
 
 
Você consegue visualizar como ficaria a solução do problema apresentado com a aplicação da 
estrutura proposta pelo padrão Prototype?
 
Em Java, todo objeto já oferece uma implementação padrão para a operação clone, conhecida pelo termo shal
low copy. Essa implementação padrão apenas copia os valores dos atributos de um objeto para sua réplica.
 
 
Se um objeto Venda, por exemplo, possuir um atributo que seja uma referência para um objeto Cliente 
relacionado, a cópia desse objeto Venda compartilhará com o objeto original a referência para o mesmo 
objeto Cliente, ou seja, em uma shallow copy, os objetos referenciados pelo objeto original não são clonados.
 
 
 
Se você precisar criar cópias dos objetos 
referenciados, deverá criar uma 
implementação específica da operação clone, 
sobrepondo a implementação padrão 
disponível na classe Object. Esse processo de 
geração da cópia de toda a árvore de objetos 
relacionados ao objeto que está sendo clonado 
é conhecido pelo termo deep copy.
 
Em nosso exemplo, vamos utilizar a cópia 
padrão já oferecida pela classe Object. 
Portanto, não precisaremos modificar as 
subclasses de MsgRegistrarCliente.
 
O próximo passo é criar e registrar as instâncias protótipo de cada subclasse de MsgRegistrarCliente, 
associando-as com a respectiva origem. Faremos isso criando um HashMap e associando o código da origem 
com a respectiva instância protótipo, conforme o código a seguir. Antes de criar um decodificador para 
mensagens XML, a fábrica cria as instâncias protótipo de cada subclasse, passando-as para o construtor da 
classe RegistrarClienteXMLDecoder.
 
Veja o exemplo a seguir.
 
 
java
public RegistrarClienteDecoder createRegistrarClienteDecoder() {
 HashMap‹String, MsgRegistrarCliente›prototypes;
 prototypes.put(“X”, new MsgRegistrarCliente_X());
 prototypes.put(“Y”, new MsgRegistrarCliente_Y());
 prototypes.put(“Z”, new MsgRegistrarCliente_Z());
 
 return new RegistrarClienteXMLDecoder(prototypes);
 } 
Agora, modificamos a classe RegistrarClienteXMLDecoder, de forma que seu construtor passe a receber 
essas instâncias das subclasses de MsgRegistrarCliente, isto é, os protótipos de cada subclasse associados 
às respectivas origens. Além disso, substituímos todo o código condicional existente na versão anterior por 
uma única chamada à operação clone da instância de MsgRegistrarCliente associada à origem recebida como 
parâmetro da operação decode.
java
public class RegistrarClienteXMLDecoder {
 private HashMap‹String, MsgRegistrarCliente› prototypes;
 
 public RegistrarClienteXMLDecoder(HashMap‹String, 
MsgRegistrarCliente›prototypes) {
 this.prototypes = prototypes;
 }
 
 public MsgRegistrarCliente decode(String textoMsg, String origem) {
 MsgRegistrarCliente prototype = prototypes.get(origem);
 
 MsgRegistrarCliente msg = (MsgRegistrarCliente) prototype.clone();
 
 // … aqui viria o código de decodificação e preenchimento dos atributos
 // do objeto MsgRegistrarCliente
 return msg;
 } 
Você percebeu que esse código, agora, pode instanciar novas subclasses de MsgRegistrarCliente, sem que 
seja necessário modificá-lo?
 
Nessa solução, a classe MsgRegistrarCliente desempenha o papel de Prototype, e cada subclasse de 
MsgRegistrarCliente tem o papel de ConcretePrototype. A classe RegistrarClienteXMLDecoder corresponde 
ao participante Client definido na estrutura do padrão.
Atividade 2
No ambiente Java, o padrão Prototype é implementado de forma simples, com o uso de polimorfismo no 
método clone. Acerca da implementação do método sobrescrito, é correto afirmar que
 A 
não é necessário o uso de new na implementação do método, já que 
Object faz a cópia de forma automática.
 
 B 
para acessar os atributos que serão copiados, deve ser utilizado o 
operador super.
 
 C 
como o método é marcado como final, ele não permite acesso à versão 
herdada, no caso de vários níveis de classes utilizando clonagem.
 
 D 
todo objeto clonável deve ser associado a um atributo 
estático.
 
 E 
os atributos clonados que forem do tipo objeto exigirão o uso de new 
ou de outras clonagens.
 
A alternativa E está correta.
Como todo objeto é ponteiro, para cada atributo do tipo objeto deve ser copiado para uma nova instância, 
o que pode ser feito com o uso de new ou de clonagem do atributo, quando também estiver implementada. 
O uso de new para o novo objeto é obrigatório; atributos internos serão acessados com this; se o método 
fosse final não iria permitir a sobrescrita; e não existe a obrigatoriedade de armazenar os objetos clonáveis 
em atributos estáticos, embora seja uma prática utilizada por algumas ferramentas de software.
Consequências e padrões relacionados ao Prototype
O padrão Prototype permite diminuir a quantidade de código no sistema, ao trabalhar com objetos já 
existentes, muitas vezes globais, para a geração dos novos objetoscom uma configuração inicial definida. No 
entanto, implementar a operação de clonagem pode ser algo complexo, já que todos os atributos internos 
que são objetos também precisam ser clonáveis. Por exemplo, o uso de Prototype em um Composite exigiria 
que todos os objetos da hierarquia fossem capazes de se clonar. Embora ocorra essa dificuldade, o padrão é 
muito útil para definir modelos preexistentes configuráveis, que servirão como ponto de partida para 
utilização em novos sistemas.
 
Explore no vídeo a seguir as consequências e padrões relacionados ao Prototype.
 
 
 
 
 
 
 
Conteúdo interativo
 
Acesse a versão digital para assistir ao vídeo.
 
 
 
 
O padrão Prototype é aplicável em pelo menos três situações específicas. Entenda!
 
 
 
 
1
Simplificação da criação de famílias de produtos
Quando há muitas fábricas específicas para diferentes famílias de 
produtos, esse padrão permite uma solução sem a necessidade de 
criar uma subclasse para cada família. Basta definir uma única classe 
fábrica e instanciar cada família configurada com os protótipos que 
serão clonados.
 
 
 
 
 
 
 
 
2
Otimização da criação de instâncias
Quando uma classe possui poucas combinações de estado, é 
mais eficiente criar instâncias típicas antecipadamente e gerar 
cópias delas, em vez de instanciá-las manualmente.
 
 
 
 
 
 
 
 
3
Criação de instâncias com estado complexo
Quando uma classe possui muitos atributos e relacionamentos, 
tornando a criação de novas instâncias custosa ou complexa, pode 
ser necessário gerar objetos com estados idênticos ou com 
pequenas variações.
 
 
 
 
Enquanto o padrão Factory Method define uma hierarquia de classes de criação paralela às classes produto 
instanciadas, o padrão Prototype elimina essa hierarquia, substituindo a chamada ao método fábrica pelo 
registro de uma instância protótipo, que é clonada posteriormente.
 
O padrão Prototype permite a criação de fábricas flexíveis que podem ter sua configuração de instâncias 
definida e modificada em tempo de execução, ao contrário da solução dada pelo padrão Abstract Factory, 
que é basicamente estática.
 
Em contrapartida, o padrão Prototype demanda que cada subclasse do produto a ser instanciado implemente 
a operação clone, o que pode ser complexo ou difícil, especialmente nos casos de utilização de classes de 
terceiros ou compartilhadas com outros sistemas.
 
Além disso, os efeitos colaterais oriundos de uma cópia baseada em uma estratégia shallow copy e a 
complexidade de implementar uma estratégia deep copy, especialmente quando existir uma árvore complexa 
de relacionamentos ou relacionamentos circulares, podem trazer dificuldades à implementação desse padrão.
Atividade 3
Em determinado sistema de controle de contas a pagar, ocorre uma organização hierárquica de centros de 
custo. Como muitas contas não variam de um mês para outro, foi utilizado o padrão Prototype para a cópia 
dos valores do mês anterior, para alterar apenas os itens que sofreram modificações em seus valores.
 
Considerando o contexto descrito, qual cuidado deve ser tomado no gerenciamento dos centros de custo, se 
eles forem organizados em um padrão Composite?
 A Impedir mais de dois níveis na hierarquia. 
 B Utilizar o modificador final em todos os atributos. 
 C Utilizar o padrão Singleton no Composite. 
 D Definir a hierarquia em um atributo estático. 
 E Tornar todas as classes utilizadas no Composite clonáveis. 
A alternativa E está correta.
Como objetos são ponteiros, é necessária uma nova instância para cada atributo que não seja um tipo 
nativo, logo as classes do Composite que foi utilizado para a hierarquia devem ser clonáveis. Não há 
restrições quanto à quantidade de níveis; o uso de final impediria alterações na cópia; um atributo estático 
iria compartilhar o mesmo objeto para todos os meses; e não há como utilizar Singleton, já que os valores 
não seriam os mesmos para meses diferentes, tratando de mais de uma hierarquia.
Intenção, problema e solução do padrão Singleton
No desenvolvimento de software, um problema comum é o gerenciamento de recursos compartilhados 
globalmente, como pools de conexão com bancos de dados e controle de acesso. O uso de múltiplos 
gestores pode causar concorrência ou inconsistência nos valores. O padrão Singleton resolve esse problema 
garantindo que a classe gestora tenha apenas uma única instância acessível globalmente. Sua implementação 
é simples: um construtor privado, uma instância privada estática e um método público estático para retornar 
essa instância.
 
Entenda no vídeo a seguir a intenção, o problema e a solução do padrão Singleton.
 
 
 
 
 
 
 
 
Conteúdo interativo
 
Acesse a versão digital para assistir ao vídeo.
 
 
 
 
Intenção do padrão Singleton
O propósito do padrão Singleton é garantir que exista uma (e apenas uma) instância de uma classe, provendo 
um ponto de acesso global a essa instância.
Problema do padrão Singleton
 
 
 
Suponha uma situação na qual você queira 
garantir que apenas uma instância de uma 
classe possa existir em determinado processo, 
como no gerenciamento de recursos como 
cache de objetos, log, conexões com banco de 
dados e objetos que representem recursos 
compartilhados por todo o processo.
 
Uma possível solução seria definir uma variável 
global, referenciando o objeto a ser 
compartilhado. Dessa forma, todos os módulos 
que precisassem desse objeto fariam o acesso 
via essa variável global compartilhada. O problema é que nada impediria outros módulos de criar múltiplas 
instâncias dessa classe.
 
 
 
 
Outra solução seria definir com o escopo de classe todas as operações da classe cujo objeto único deve ser 
compartilhado. Em Java, isso significa definir todas as operações da classe com o modificador static. Essa 
solução, porém, não é flexível, pois não admite a definição de subclasses e a utilização de polimorfismo.
 
 
 
Polimorfismo
Princípio do modelo orientado a objetos, pelo qual duas ou mais subclasses de uma mesma superclasse 
podem conter métodos com a mesma assinatura, mas com implementações diferentes, resultando em 
comportamentos especializados para cada subclasse.
Solução do padrão Singleton
O diagrama a seguir apresenta a estrutura do padrão Singleton.
Estrutura geral do padrão Singleton.
O nome Singleton representa o nome da classe que você deseja que tenha apenas uma instância. O atributo u
nicaInstancia é uma referência a essa única instância a ser compartilhada pelos demais módulos.
 
O construtor dessa classe deve ser privativo, garantindo que outros módulos não possam instanciá-la 
diretamente. Tanto a operação Instancia quanto o atributo unicaInstancia são propriedades com escopo de 
classe (static).
 
Um possível uso do padrão Singleton consiste na implementação do padrão Abstract Factory. Veja no 
próximo código a implementação de uma fábrica concreta utilizando o padrão Singleton. A instância 
compartilhada é referenciada pelo atributo factory, definido com o modificador static. O construtor da classe 
é definido como private, o que impede que ela seja diretamente instanciada em outros módulos. A operação 
getFactory retorna a instância única compartilhada.
java
publicclass XMLDecoderFactory extends DecoderFactory {
 private static DecoderFactory factory = null;
 private XMLDecoderFactory() {
 }
 public static DecoderFactory getFactory() {
 if (factory == null)
 factory = new XMLDecoderFactory();
 return factory;
 }
 public abstract RegistrarClienteDecoder createRegistrarClienteDecoder() {
 return new RegistrarClienteXMLDecoder();
 } 
Podemos definir a fábrica abstrata como um registro dos diversos singletons correspondentes às fábricas 
concretas.
 
No exemplo, a seguir, as fábricas concretas são registradas em um HashMap codificado pela origem (X, Y ou 
Z). Cada entrada dessa estrutura de dados associa uma origem ao singleton da respectiva fábrica concreta. A 
operação getInstance acessa essa estrutura para retornar a fábrica concreta correspondente à origem 
recebida como parâmetro.
java
public abstract class DecoderFactory {
 private static HashMap‹String, DecoderFactory› factoryMap;
 
 static {
 factoryMap = new HashMap‹›();
 factoryMap.put(“X”, XMLDecoderFactory.getInstance());
 factoryMap.put(“Y”, CSVDecoderFactory.getInstance());
 factoryMap.put(“Z”, TextoLivreDecoderFactory.getInstance());
 }
 
 public static DecoderFactory getInstance(String origem) {
 return factoryMap.get(origem);
 }
 
 public abstract RegistrarClienteDecoder 
createRegistrarClienteDecoder();
 public abstract RegistrarContaDecoder 
createRegistrarContaDecoder();
 } 
Atividade 4
Assinale a alternativa que expressa a intenção do padrão de projeto Singleton:
 A 
Permitir a instanciação de um objeto a partir da clonagem de outro 
objeto já existente.
 
 B 
Permitir a criação de famílias de produtos relacionados, de forma que o módulo 
cliente dependa apenas das interfaces genéricas desses produtos.
 
 C 
Garantir que uma classe tenha apenas uma instância em um processo de 
execução, impedindo que outros módulos possam instanciar diretamente essa 
classe.
 
 D 
Permitir que um módulo utilize uma operação abstrata de criação de objetos 
definida em uma superclasse e implementada de forma específica em cada 
subclasse.
 
 E 
Permitir que um único processo de construção possa instanciar diferentes 
representações de um objeto complexo.
 
A alternativa C está correta.
As alternativas A, B, D e E correspondem, respectivamente, às intenções dos padrões Prototype, Abstract 
Factory, Factory Method e Builder.
Consequências e padrões relacionados ao Singleton
O Singleton centraliza responsabilidades, mas exige cuidados, como o uso de semáforo na instanciação do 
atributo estático para evitar múltiplas instâncias em ambientes concorrentes. Outra limitação é a dificuldade 
em testes unitários, pois o padrão não prevê instâncias simuladas. Embora não seja estruturado com base em 
outros padrões, pode encapsular funcionalidades de Builder e Factory Method na construção de objetos.
 
Explore no vídeo a seguir as consequências e padrões relacionados ao Singleton.
 
 
 
 
 
 
 
Conteúdo interativo
 
Acesse a versão digital para assistir ao vídeo.
 
 
 
 
O padrão Singleton permite o acesso controlado a uma única instância de uma classe, sendo uma solução 
superior à utilização de variáveis globais. Permite, inclusive, a criação de subclasses mais específicas sem 
impacto para os módulos que utilizam a instância Singleton.
 
 
 
Comentário
 
O padrão Singleton é frequentemente utilizado em conjunto com o padrão Abstract Factory.
 
 
 
Após o surgimento de abordagens fortemente baseadas na construção de testes unitários automatizados e 
na aplicação de princípios como o da inversão de dependências, o padrão Singleton passou a ser visto como 
um potencial problema. Ele pode dificultar a implementação de testes unitários, visto que a unidade a ser 
testada pode estar acoplada a Singletons que dificultam o isolamento da unidade em relação às suas 
dependências.
 
Além disso, existem linguagens que permitem quebrar o objetivo original do padrão, pois construções como 
reflection e serialização permitem a criação independente de objetos de classes Singleton.
 
Portanto, esse é um padrão que deve ser utilizado apenas em casos muito específicos para não criar 
acoplamentos desnecessários que tornem a estrutura do software menos flexível e dificultem o processo de 
testes e depuração dos módulos.
Singleton: Pattern ou Anti-Pattern
 
 
 
O padrão Singleton oferece algumas 
possibilidades interessantes no 
desenvolvimento de software, como a 
definição de um ponto global de acesso, com a 
utilização de uma instância única para 
determinada classe. É um padrão de fácil 
implementação e que promove economia de 
recursos, ao mesmo tempo em que facilita o 
gerenciamento de dados compartilhados por 
todo o aplicativo.
 
No entanto, ocorre um aumento do 
acoplamento do código, que fica restrito ao uso de um objeto específico, além de dificultar os testes, já que 
os estados gerenciados são globais para o aplicativo e qualquer mudança pontual irá impactar em todos os 
processos em execução que os utilize. Além disso, como o mesmo objeto é utilizado por todos os processos, 
exige uma grande preocupação em termos de concorrência e distribuição de processamento, podendo até 
mesmo se tornar inviável para ambientes com execução em cluster.
 
 
Atividade 5
O padrão Singleton é bastante simples e muitas vezes pode ser utilizado como um substituto para objetos 
globais. Entre as características positivas ou negativas do Singleton, é correto afirmar que
 A diminui o acoplamento do código. 
 B facilita a execução de testes. 
 C os estados gerenciados são globais para o aplicativo. 
 D 
é voltado para ambientes de alta concorrência e processamento 
distribuído.
 
 E 
deve permitir a clonagem, obedecendo ao padrão 
Prototype.
 
A alternativa C está correta.
Como o Singleton permite apenas uma instância para a classe, os estados gerenciados se tornam globais 
para o aplicativo. Ocorre um aumento do acoplamento, devido à restrição de uso para um objeto 
específico; dificulta a execução de testes, já que não é possível criar um objeto simulado; apresenta 
dificuldades em termos de concorrência e distribuição de processamento; e não permite clonagem, pois 
contraria o próprio princípio do Singleton, em que temos apenas uma instância.
 
 
5. Conclusão
 
 
Considerações finais
O que você aprendeu neste conteúdo?
O padrão de projeto de criação Factory Method.
O padrão de projeto de criação Abstract Factory.
O padrão de projeto de criação Builder.
O padrão de projeto de criação Prototype.
O padrão de projeto de criação Singleton.
 
 
Podcast
 
Para finalizar, entenda por que é importante conhecer os padrões GOF de criação e ouça sobre os 
principais pontos do conteúdo estudado.Conteúdo interativo
 
Acesse a versão digital para ouvir o áudio.
 
 
 
 
 
• 
• 
• 
• 
• 
 
Explore +
Para saber mais sobre a programação orientada a objetos, acesse o site da DevMedia e leia o artigo intitulado 
Utilização dos princípios SOLID na aplicação de padrões de projeto.
 
O site Padrões de projeto/Design patterns – Refactoring apresenta um conteúdo interativo e bastante 
completo de todos os padrões GOF com exemplos de código em diversas linguagens de programação.
 
Já o livro Java EE 8 Design Patterns and Best Practices, de Rhuan Rocha e João Purificação, aborda padrões 
para Java EE, incluindo interface, lógica de negócio, integração de sistemas e microsserviços.
Referências
GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J. Design Patterns: Elements of Reusable Object-Oriented 
Software. 1. ed. Boston: Addison-Wesley, 1994.
 
MARTIN, R. C. Clean Architecture: A Craftsman´s Guide to Software Structure and Design. 1. ed. Upper Saddle 
River, NJ: Prentice Hall, 2017.
 
METSKER, S. J.; WAKE, W. C. Design Patterns in Java. 1.ed. Boston: Addison-Wesley, 2006.
 
 
	Padrões GOF de criação
	1. Itens iniciais
	Objetivos
	Introdução
	Conteúdo interativo
	1. Padrão de projeto Factory Method
	Intenção e problema do padrão Factory Method
	Conteúdo interativo
	hasNext
	next
	remove
	Atenção
	Atividade 1
	Solução do padrão Factory Method
	Conteúdo interativo
	ArrayList
	LinkedList
	HashSet
	TreeSet
	Atividade 2
	Consequências e padrões relacionados ao Factory Method
	Conteúdo interativo
	Dica
	Atividade 3
	2. Padrão de projeto Abstract Factory
	Intenção e problema do padrão Abstract Factory
	Conteúdo interativo
	XML
	Texto Fixo
	CSV
	Atividade 1
	Solução do padrão Abstract Factory
	Conteúdo interativo
	Atividade 2
	Consequências e padrões relacionados ao Abstract Factory
	Conteúdo interativo
	Recomendação
	Atividade 3
	Abstract Factory X Injeção de dependências
	Conteúdo interativo
	Atividade 4
	3. Padrão de projeto Builder
	Intenção e problema do padrão Builder
	Conteúdo interativo
	Intenção do padrão Builder
	Problema do padrão Builder
	Construir
	Listar
	Gerar
	Atividade 1
	Solução do padrão Builder
	Conteúdo interativo
	Concessão de acesso
	Acesso ao produto
	Estrutura dos produtos
	Atividade 2
	Consequências e padrões relacionados ao Builder
	Conteúdo interativo
	Aplicações do padrão de projeto Builder
	Atividade 3
	4. Padrões de projeto Prototype e Singleton
	Intenção e problema do padrão Prototype
	Conteúdo interativo
	Intenção do padrão Prototype
	Problema do padrão Prototype
	Atividade 1
	Solução do padrão Prototype
	Conteúdo interativo
	Atividade 2
	Consequências e padrões relacionados ao Prototype
	Conteúdo interativo
	Simplificação da criação de famílias de produtos
	Otimização da criação de instâncias
	Criação de instâncias com estado complexo
	Atividade 3
	Intenção, problema e solução do padrão Singleton
	Conteúdo interativo
	Intenção do padrão Singleton
	Problema do padrão Singleton
	Solução do padrão Singleton
	Atividade 4
	Consequências e padrões relacionados ao Singleton
	Conteúdo interativo
	Comentário
	Singleton: Pattern ou Anti-Pattern
	Atividade 5
	5. Conclusão
	Considerações finais
	O que você aprendeu neste conteúdo?
	Podcast
	Conteúdo interativo
	Explore +
	Referênciasda
solução. O downcasting está presente na
conversão da coleção de itens para ArrayList ou
para HashSet, dependendo do tipo da coleção recebida como parâmetro.
 
Perceba como essa implementação adiciona complexidade em relação à implementação anterior. Você deve
ter sempre em mente que alta complexidade também é um dos principais inimigos da evolução sustentável de
um sistema.
 
O problema específico consiste em implementar o método removerItensInvalidos, e todos os demais métodos
nos quais você precise percorrer e interagir com uma coleção de objetos, de modo que ele funcione com
qualquer forma de organização dessa coleção, sem que haja necessidade de recorrer a soluções baseadas em
clonagem ou em estruturas condicionais complexas presentes nos exemplos apresentados.
Atenção
O problema mais geral resolvido pelo Factory Method é fazer com que um módulo cliente não precise
instanciar diretamente uma dentre várias possíveis implementações de uma abstração, tornando-o,
portanto, dependente apenas da abstração e não de suas implementações específicas. 
Atividade 1
Assinale a alternativa que expressa a intenção do padrão de projeto Factory Method.
A Permitir a criação de famílias de objetos relacionados ou dependentes por meio de uma interface
baseada em produtos genéricos especializados pelas diferentes famílias.
B
Permitir que um módulo possa criar objetos de uma hierarquia de classes, chamando uma operação
genérica definida em uma superclasse e deixando a instanciação do objeto específico da hierarquia para
cada implementação dessa operação nas subclasses.
C Garantir que exista apenas uma instância de determinada classe, impedindo que outros módulos
possam acessar diretamente o construtor dessa classe.
D Permitir a instanciação de objetos por meio da geração de cópias de objetos já existentes.
E Permitir a reutilização de um processo de construção de objetos complexos, definindo o processo de
criação e as diferentes representações desses objetos em módulos distintos.
A alternativa B está correta.
O padrão Factory Method define uma interface para a criação de objetos e deixa para as subclasses
específicas a decisão sobre sua instanciação.
Solução do padrão Factory Method
Esse padrão define um método responsável por padronizar a criação de objetos para uma família de classes
ou para aquelas que implementam uma interface específica. O padrão Factory Method é amplamente usado
em frameworks, especialmente quando é necessário criar interfaces de acesso a recursos baseados em
middlewares de diferentes fornecedores. Além disso, é comum em bibliotecas Java, como na classe Calendar,
cujo método getInstance retorna o tipo de calendário adequado conforme a localidade e o fuso horário.
 
Compreenda no vídeo a seguir a solução do padrão Factory Method.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
O framework de estrutura de dados da linguagem Java implementa uma solução para esse problema por meio
da aplicação do padrão Factory Method.
 
As estruturas de dados são classes que implementam uma interface genérica chamada Collection. Aqui estão
exemplos de diferentes implementações dessa interface. Veja!
ArrayList
Representa estruturas como arrays.
LinkedList
Representa estruturas como listas encadeadas.
HashSet
Representa estruturas como conjuntos chave-valor.
TreeSet
Representa estruturas como conjuntos organizados em árvores de busca.
A organização dessas classes está ilustrada no diagrama de classes a seguir.
Diagrama de classes.
A interface Collection define uma operação abstrata chamada iterator, implementada em cada estrutura de
dados específica. Essa operação cria e retorna um objeto que implementa a interface Iterator.
 
A interface Iterator define um cursor que possibilita a navegação em uma coleção de dados e a exclusão de
elementos com as mesmas operações apresentadas nos exemplos anteriores, isto é, hasNext, next e remove.
 
Você deve ter percebido que a implementação do percurso depende da forma com que os dados são
organizados na coleção. A remoção de um elemento da coleção também é dependente da forma como seus
elementos são estruturados. Isso significa que existe uma implementação da interface Iterator para cada
classe que implementa a interface Collection, como você pode ver na imagem a seguir.
Interface Iterator por classe.
Dessa forma, a operação iterator de ArrayList instancia um ArrayIterator, a de LinkedList instancia um 
ListIterator, e assim por diante, para cada coleção específica. Esse esquema é uma simplificação, para fins
didáticos, das classes realmente implementadas na linguagem Java.
 
Veja, no código a seguir, como você poderia implementar a operação removerItensInvalidos usando esse
framework!
java
public void removerItensInvalidos(Collection‹ItemPedido› itens) {
 Iterator‹ItemPedido› iterator = itens.iterator();
 while (iterator.hasNext()) {
 ItemPedido item = iterator.next();
 if (! isValido(item)) {
 itens.remove(item);
 }
 }
 } 
O comando itens.iterator() é uma chamada polimórfica a partir da interface Collection, que resulta na criação
de uma das implementações específicas da interface Iterator.
 
Portanto, se a Collection for um ArrayList, por exemplo, esse comando será executado pela classe ArrayList,
que criará um ArrayIterator e retornará essa instância (cursor), a qual será referenciada pela variável iterator.
 
Note que, nessa solução, a única responsabilidade da operação iterator é criar (fabricar) a instância da
interface Iterator apropriada para a estrutura de dados. Esta é a ideia central do padrão Factory Method:
definir uma operação “fábrica” na classe abstrata (Collection), deixando para cada subclasse (ArrayList,
LinkedList, HashSet, TreeSet) a decisão da implementação específica da interface (Iterator) retornada pela
fábrica.
 
Observe que a estrutura geral da solução proposta pelo padrão Factory Method define quatro participantes:
Estrutura geral da solução proposta pelo padrão Factory Method.
Do lado esquerdo, estão os produtos a serem fabricados. O participante Product corresponde ao tipo genérico
do elemento a ser fabricado, enquanto ConcreteProduct corresponde a cada especialização do produto a ser
fabricado.
 
No exemplo das estruturas de dados, a interface Iterator tem o papel de Product, enquanto as classes
ArrayIterator, ListIterator, KeyIterator e ValueIterator têm o papel de ConcreteProduct.
 
Do lado direito, estão os criadores, isto é, as classes responsáveis pela instanciação dos produtos. O
participante Creator define uma operação (factoryMethod) que retorna uma instância da interface genérica 
Product, enquanto ConcreteCreator representa cada implementação concreta de Creator responsável pela
instanciação do ConcreteProduct específico.
 
Portanto, no exemplo das estruturas de dados, a interface Collection corresponde ao participante Creator, e
sua operação abstrata iterator é o factoryMethod. Já as classes ArrayList, LinkedList, HashSet e TreeSet têm
o papel de ConcreteCreator e são responsáveis pela implementação da operação factoryMethod, na qual será
feita a instanciação de um ArrayIterator, ListIterator, KeyIterator e ValueIterator (ConcreteProduct),
respectivamente.
Atividade 2
Em relação à estrutura de solução proposta pelo padrão de projeto Factory Method, assinale a alternativa
correta.
A Inclui o participante Creator, uma classe concreta responsável pela criação de produtos organizados em
uma hierarquia que especializa um participante abstrato Product.
B O método Factory Method é implementado no participante Product.
C
Consiste em uma hierarquia de classes que especializam um participante abstrato Creator, responsável
por criar produtos que não compartilham uma superclasse comum ou não implementam uma interface
genérica.
D É composta por uma hierarquia de classes que especializam um participante abstrato Creator e por outra
hierarquia de classesque especializam um participante abstrato Product.
E O método Factory Method é implementado no participante Creator.
A alternativa D está correta.
Creator é uma classe abstrata ou uma interface. O Factory Method não é implementado em Product, mas,
sim, em cada ConcreteCreator. Os produtos formam uma hierarquia com uma superclasse ou interface
genérica comum. O Factory Method é uma operação declarada em Creator, mas implementada apenas nos
participantes do tipo ConcreteCreator.
Consequências e padrões relacionados ao Factory Method
A principal consequência do uso de Factory Method é o aumento da extensibilidade, já que novas classes
podem ser utilizadas para o mesmo domínio com facilidade. Além disso, existem outras características
interessantes, como a conexão de hierarquias paralelas de classes, com base em uma interface-padrão, e o
desacoplamento entre a criação do objeto e o conhecimento acerca do tipo concreto desse objeto. 
 
Seu poder aumenta quando combinado com outros padrões, como Strategy, que define estratégias
específicas para cada tipo concreto, ou Template Method, que centraliza etapas comuns da criação enquanto
delega a especialização às subclasses.
 
Explore no vídeo a seguir as consequências e padrões relacionados ao Factory Method.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
O padrão Factory Method permite que diferentes implementações de um mesmo serviço possam ser
utilizadas por um cliente sem que seja necessário replicar códigos similares ou utilizar estruturas condicionais
complexas, conforme já vimos.
 
Além disso, esse padrão possibilita a conexão de duas hierarquias paralelas representadas pelos participantes
genéricos Creator e Product.
Dica
O Factory Method é muito útil quando precisamos segregar uma hierarquia de objetos detentores de
informações (objetos de domínio) dos diferentes algoritmos de manipulação dessas informações. 
O padrão Factory Method pode ser aplicado em conjunto com o padrão Strategy, que tem como objetivo a
separação de diferentes algoritmos dos objetos de domínio sobre os quais eles atuam.
 
Template Method é outro padrão frequentemente utilizado em conjunto com o Factory Method. Trata-se de
uma implementação genérica definida em uma superclasse que contém passos que podem ser especializados
nas subclasses. Um desses passos pode corresponder à criação de um objeto específico, que pode ser
realizada pela aplicação do padrão Factory Method.
 
Um exemplo em que a combinação entre Factory Method e Strategy se torna muito útil é no processo de
pagamento para as cestas virtuais de compras. Embora tenham o mesmo objetivo, cada forma de pagamento
exige uma estratégia específica, que pode ser encapsulada em uma classe própria.
 
O código Java seguinte exemplifica o contexto descrito.
java
// Padrão Strategy
public interface PagamentoStrategy {
 void pagar(double valor);
}
public class PagamentoCartao implements PagamentoStrategy {
 @Override
 public void pagar(double valor) {
 System.out.println("Pagamento de " + valor + " com Cartão de Crédito.");
 }
}
public class PagamentoPix implements PagamentoStrategy {
 @Override
 public void pagar(double valor) {
 System.out.println("Pagamento de " + valor + " com Pix.");
 }
}
// Padrão Factory Method
public abstract class PagamentoFactory {
 public abstract PagamentoStrategy criarMetodoPagamento();
}
public class CartaoFactory extends PagamentoFactory {
 @Override
 public PagamentoStrategy criarMetodoPagamento() {
 return new PagamentoCartao();
 }
}
public class PixFactory extends PagamentoFactory {
 @Override
 public PagamentoStrategy criarMetodoPagamento() {
 return new PagamentoPix();
 }
}
// Classe Cliente
public class ServicoPagamento {
 private PagamentoStrategy pagamento;
 public ServicoPagamento (PagamentoFactory factory) {
 this. pagamento = factory.criarMetodoPagamento();
 }
 public void executarPagamento(double valor) {
 pagamento.pagar(valor);
 }
 public static void main(String[] args) {
 new ServicoPagamento(new CartaoFactory()).executarPagamento(100);
 }
} 
Atividade 3
Alguns padrões podem ser combinados com o Factory Method, incluindo o Template Method. Assinale a
seguir a alternativa que descreve o procedimento correto para que seja utilizado o Template Method em um
padrão Factory Method.
A Definir uma família de classes no padrão Command e invocar seus objetos no Template Method
implementado na classe Creator.
B É obrigatório o uso do padrão Strategy ao adotar o Template Method.
C Para que seja possível utilizar o Template Method, o método de construção precisa ser estático e
abstrato na classe Creator.
DConcentrar o procedimento comum na classe abstrata Creator, com lacunas definidas por métodos
abstratos e implementar esses métodos abstratos em cada criador concreto do sistema.
E Todos os métodos abstratos, relacionados ao Template Method, devem utilizar o modificador final.
A alternativa D está correta.
O Template Method define um processo geral com lacunas que devem ser preenchidas pelos
descendentes, completando a funcionalidade para o contexto de utilização. Quanto às demais opções: uma
família de classes no padrão Command poderia ser utilizada junto ao Template Method, mas deve ser
aplicada nos construtores concretos e não na classe abstrata Creator; o uso do padrão Strategy não é
obrigatório, já que a alternância de funcionalidades é definida com base na implementação dos métodos
abstratos; o método de construção não precisa ser abstrato, mas sim os métodos associados às lacunas de
processamento, também o uso de um modificador abstract impede o uso de static; e o modificador final
não é utilizado em métodos abstratos, pois impede a sobrescrita deles.
2. Padrão de projeto Abstract Factory
Intenção e problema do padrão Abstract Factory
Em muitos casos, o processo de construção de um produto segue diretrizes gerais, mas sua implementação
varia entre fornecedores, como ocorre na indústria automotiva. O padrão Abstract Factory se aplica a esse
cenário, como no Enterprise Java Beans 2.0, em que o framework define a interface EJBHome para criar
interfaces remotas EJBObject, usadas no acesso ao pool de objetos corporativos. O programador apenas
especializa essas interfaces para definir fábricas de conexões remotas para seus objetos de negócio,
derivados de SessionBean.
 
Entenda no vídeo a seguir a intenção e o problema do padrão Abstract Factory.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Abstract Factory é um padrão que fornece uma interface para a criação de famílias de objetos relacionados ou
dependentes, sem criar dependências entre o cliente e as classes concretas instanciadas.
Imagine que você esteja trabalhando em uma
implementação que tenha integrações com
sistemas externos de diferentes organizações.
Considere que os sistemas de cada
organização externa enviem os mesmos tipos
de mensagens em diferentes formatos, como
texto contendo campos de tamanho
predefinido, XML (EXtended Markup Language),
CSV (Comma Separated Values), entre outros.
Considere, ainda, que cada organização envie
suas mensagens sempre no mesmo formato. 
 
O quadro a seguir apresenta um exemplo de
integração com três organizações que enviam mensagens codificadas no formato especificado.
Emissor Mensagem Formato
Organização X Registrar cliente XML
Organização Y Registrar cliente CSV
Organização Z Registrar cliente Campos de tamanho fixo
Organização X Registrar conta XML
Organização Y Registrar conta CSV
Organização Z Registrar conta Campos de tamanho fixo
Alexandre Correa.
Podemos definir um conjunto de classes responsáveis pela decodificação de mensagens de um formato
específico em objetos independentes desse formato. Confira na imagem a seguir a estrutura dessa solução.
Conjunto de classes.
A interface RegistrarClienteDecoder representa um serviço genéricoque traduz uma mensagem de registro
de cliente, recebida em um formato qualquer, para um objeto da classe MsgRegistrarCliente, que corresponde
à representação independente de formato da mensagem recebida.
 
As implementações dessa interface para cada formato específico são:
XML
Classe RegistrarClienteXMLDecoder
Texto Fixo
Classe RegistrarClienteTextoFixoDecoder
CSV
Classe RegistrarClienteCSVDecoder
Para cada mensagem recebida pelo sistema (como Registrar cliente, Registrar conta), deve ser criada uma
estrutura de classes similar à apresentada.
 
O código a seguir corresponde ao esqueleto de implementação da classe ServicoIntegracao: um exemplo de
módulo que utiliza as classes de decodificação. As operações dessa classe representam as mensagens
recebidas das diferentes organizações.
 
A operação registrarCliente, por exemplo, recebe um texto com o conteúdo da mensagem enviada por uma
origem. Essa origem é codificada em um texto (X, Y ou Z), representando as diferentes organizações. O texto
da mensagem deve ser decodificado do formato específico em uma instância da classe MsgRegistrarCliente
para seu posterior tratamento.
java
public class ServicoIntegracao {
 public void registrarCliente (String textoMsg, String origem) {
 RegistrarClienteDecoder msgDecoder = null;
 
 if (“X”.equals(origem)) {
 msgDecoder = new RegistrarClienteXMLDecoder();
 } else if ("Y".equals(origem)) {
 msgDecoder = new RegistrarClienteCSVDecoder();
 } else if ("Z".equals(origem)) {
 msgDecoder = new RegistrarClienteTextoFixoDecoder();
 }
 MsgRegistrarCliente msg = msgDecoder.decode(textoMsg);
 ...
 // código para o tratamento da mensagem recebida
 }
 
 public void registrarConta (String textoMsg, String origem) {
 RegistrarContaDecoder msgDecoder = null;
 
 if (“X”.equals(origem)) {
 msgDecoder = new RegistrarContaXMLDecoder();
 } else if ("Y".equals(origem)) {
 msgDecoder = new RegistrarContaCSVDecoder();
 } else if ("Z".equals(origem)) {
 msgDecoder = new RegistrarContaTextoFixoDecoder();
 }
 MsgRegistrarConta msg = msgDecoder.decode(textoMsg);
 ...
 // código para o tratamento da mensagem recebida
 }
 ... // operações para recepção e tratamento das demais mensagens
 } 
Você consegue identificar o problema dessa implementação da classe ServicoIntegracao?
 
Ela está acoplada com todos os tipos possíveis de decodificadores, concentrando toda a complexidade de
resolução sobre o decodificador apropriado para traduzir uma mensagem vinda de determinada origem. Você
deve ter percebido que, caso novos formatos e origens sejam adicionados, esse código terá de ser
modificado, o que configura uma violação do princípio Open Closed, um dos princípios SOLID.
 
Nesse exemplo, temos várias famílias de decodificadores de acordo com o formato da mensagem, como:
decodificadores XML, CSV e Texto Fixo. Ao recebermos uma mensagem da origem X, por exemplo, sabemos
que precisamos utilizar o conversor XML correspondente a essa mensagem, pois essa origem envia todas as
suas mensagens no formato XML.
 
Portanto, o problema tratado pelo padrão Abstract Factory consiste em: 
 
Isolar o cliente de uma família de produtos relacionados de suas implementações específicas.
 
Responder à pergunta: como podemos remover todas as instanciações dos decodificadores da classe
ServicoIntegracao, criando uma solução genérica que permita que esse serviço trabalhe com novos
formatos de decodificação sem que seu código precise ser alterado?
• 
• 
Atividade 1
Assinale a alternativa que expressa a intenção do padrão de projeto Abstract Factory.
APermitir a instanciação de objetos por meio de uma interface genérica de clonagem que possibilita que
um objeto já existente gere uma cópia de seu conteúdo e a retorne para o módulo cliente.
B
Definir uma estrutura de criação de objetos em uma hierarquia de classes, em que uma operação genérica
de criação de um produto é definida na superclasse, deixando para as subclasses a decisão do produto
específico a ser instanciado.
CSeparar, em classes diferentes, o processo de construção de um objeto complexo de suas possíveis
representações, permitindo que um único processo possa criar diferentes representações de objetos.
D Fornecer um ponto de acesso global e controlado para a única instância de uma classe, impedindo que
outros módulos possam fazer a criação de objetos dessa classe diretamente.
EPermitir a criação de famílias de produtos relacionados ou dependentes, de modo que não sejam criadas
dependências entre os módulos clientes e as classes concretas das famílias específicas de produtos.
A alternativa E está correta.
A alternativa correta expressa a intenção do padrão Abstract Factory de criar interface sem dependência
entre o cliente e as classes instanciadas. As alternativas de A a D correspondem, respectivamente, às
intenções dos padrões Prototype, Factory Method, Builder e Singleton.
Solução do padrão Abstract Factory
Seguindo um modelo comum em frameworks, o padrão Abstract Factory define uma fábrica abstrata para
gerar produtos abstratos, frequentemente usados com o Template Method. O programador implementa a
interface do produto e cria a fábrica concreta, alinhando-se aos processos genéricos do framework, como
validação de usuário, middleware e segurança. Ao mesmo tempo, mantém as características específicas do
sistema nos elementos concretos.
 
Compreenda no vídeo a seguir a solução do padrão Abstract Factory.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
A estrutura da solução proposta pelo padrão Abstract Factory está representada no diagrama de classes a
seguir. 
Estrutura geral da solução proposta pelo padrão Abstract Factory.
Do lado direito, estão os vários produtos criados pelas fábricas. Cada tipo de produto é definido por uma
interface genérica (AbstractProduct_A e AbstractProduct_B) e possui diversas implementações que definem
os objetos específicos a serem criados pelas fábricas. Product_A1 e Product_A2, por exemplo, são
implementações concretas de AbstractProduct_A.
 
No problema apresentado anteriormente, a interface RegistrarClienteDecoder corresponde ao participante
AbstractProduct_A, enquanto as classes RegistrarClienteXMLDecoder, RegistrarClienteTextoFixoDecoder e
RegistrarClienteCSVDecoder representam os produtos concretos Product_A1, Product_A2 e Product_A3.
 
Da mesma forma, a interface RegistrarContaDecoder corresponde ao participante AbstractProduct_B,
enquanto as classes RegistrarContaXMLDecoder, RegistrarContaTextoFixoDecoder e
RegistrarContaCSVDecoder representam os produtos concretos Product_B1, Product_B2 e Product_B3.
 
Do lado esquerdo, estão as fábricas. Cada fábrica é responsável por criar instâncias específicas de uma
família definida por seus produtos abstratos.
 
Desse modo, a fábrica ConcreteFactory_1 é responsável por criar instâncias das classes Product_A1 e
Product_B1, enquanto a fábrica ConcreteFactory_2 é responsável por criar instâncias das classes Product_A2
e Product_B2. Portanto, Product_A1 e Product_B1 formam uma família de produtos, enquanto Product_A2 e
Product_B2 formam outra família de produtos.
Você consegue visualizar como ficaria uma nova solução para o serviço de integração com a
aplicação da estrutura proposta pelo padrão Abstract Factory?
Primeiro, precisamos definir as fábricas. Podemos organizar as famílias de decodificadores de acordo com o
formato das mensagens. Veja!
Famílias de decodificadores.
A classe DecoderFactory representa o participante AbstractFactory do padrão, definindo uma interface
genérica para a criação dos diversos decodificadores de mensagens. Cada especialização dessa classe
corresponde ao participanteConcreteFactory do padrão, sendo responsável pela criação dos decodificadores
correspondentes a um formato específico de mensagem (XML, CSV ou TextoFixo).
 
A implementação dessa estrutura é apresentada a seguir. Note que a responsabilidade de cada fábrica é
apenas instanciar a classe de um decodificador específico da família (XML, CSV etc.). A fábrica abstrata
possui uma operação adicional (fabricaParaOrigem) que retorna a fábrica apropriada para determinada
origem. Ela funciona como uma espécie de registro de todas as fábricas e suas respectivas origens.
java
public abstract class DecoderFactory {
 public abstract RegistrarClienteDecoder createRegistrarClienteDecoder();
 public abstract RegistrarContaDecoder createRegistrarContaDecoder();
 
 public static DecoderFactory fabricaParaOrigem(String origem) {
 if (“X”.equals(origem)) {
 return new XMLDecoderFactory();
 } else if ("Y".equals(origem)) {
 return new CSVDecoderFactory();
 } else if ("Z".equals(origem)) {
 return new TextoFixoDecoderFactory();
 }
 }
 }
 
 public class XMLDecoderFactory extends DecoderFactory {
 public RegistrarClienteDecoder createRegistrarClienteDecoder() {
 return new RegistrarClienteXMLDecoder();
 }
 public RegistrarContaDecoder createRegistrarContaDecoder() {
 return new RegistrarContaXMLDecoder();
 }
 }
 
 public class CSVDecoderFactory extends DecoderFactory {
 public RegistrarClienteDecoder createRegistrarClienteDecoder() {
 return new RegistrarClienteCSVDecoder();
 }
 public RegistrarContaDecoder createRegistrarContaDecoder() {
 return new RegistrarContaCSVDecoder();
 }
 } 
Agora, vamos utilizar as fábricas para modificar a implementação das operações da classe ServicoIntegracao.
Veja, no código a seguir, que a operação registrarCliente chama a operação fabricaParaOrigem, a partir da
origem recebida como parâmetro, para obter a instância da fábrica apropriada para as mensagens recebidas
dessa origem.
 
Na sequência, a chamada para a operação createRegistrarClienteDecoder da fábrica cria o decodificador
específico para a mensagem RegistrarCliente.
java
public class ServicoIntegracao {
 public void registrarCliente (String textoMsg, String origem) {
 DecoderFactory decoderFactory = DecoderFactory.fabricaParaOrigem(origem);
 RegistrarClienteDecoder msgDecoder = 
decoderFactory.createRegistrarClienteDecoder();
 MsgRegistrarCliente msg = msgDecoder.decode(textoMsg);
 ...
 // código para tratamento da mensagem MsgRegistrarCliente
 }
 
 public void registrarConta (String textoMsg, String origem) {
 DecoderFactory decoderFactory = DecoderFactory.fabricaParaOrigem(origem);
 RegistrarContaDecoder msgDecoder = 
decoderFactory.createRegistrarContaDecoder();
 MsgRegistrarConta msg = msgDecoder.decode(textoMsg);
 ...
 // código para tratamento da mensagem MsgRegistrarConta
 }
 
 ... // código para demais mensagens
 } 
Note que esse código não precisará ser modificado para novos formatos de mensagem, pois bastará adicionar
novos decodificadores e definir uma nova fábrica concreta. Além disso, a estrutura do código ficou muito mais
enxuta e menos complexa.
Esse padrão é utilizado, por exemplo, na implementação do framework AWT de interface com o usuário da
linguagem Java.
AWT
Abstract Window Toolkit é o toolkit gráfico original da linguagem de programação Java. 
Os componentes visuais específicos de plataforma, como Windows e Motif, por exemplo, formam uma família
de produtos (Button, Frame, Panel etc.). A classe Toolkit corresponde à fábrica abstrata que oferece
operações de criação de cada componente visual. Cada plataforma é implementada em uma subclasse de
Toolkit específica.
Motif
Interface gráfica padrão para usuários de sistema operacional Unix.
Atividade 2
Em relação à estrutura de solução proposta pelo padrão de projeto Abstract Factory, assinale a alternativa
correta.
A Devemos definir uma classe fábrica concreta para cada produto concreto de uma família de produtos.
B Devemos definir uma classe fábrica concreta para cada hierarquia de produtos correspondente a cada
superclasse abstrata de um produto.
C Devemos definir uma única classe fábrica concreta para todas as famílias de produtos.
D Devemos definir uma classe fábrica concreta para cada família de produtos.
E A classe abstrata de cada família de produtos deve implementar a instanciação de sua fábrica
concreta de produtos.
A alternativa D está correta.
Cada fábrica concreta é responsável pela instanciação de todos os produtos concretos de uma família, e
não de todas as famílias. Os produtos não instanciam as fábricas, e sim o oposto, isto é, as fábricas
instanciam os produtos.
Consequências e padrões relacionados ao Abstract Factory
A principal consequência do padrão Abstract Factory é a definição de modelos formais para geração de
objetos em contextos específicos de utilização, sem que ocorra perda de flexibilidade. Na prática, todo o
processamento comum é concentrado na fábrica e nos produtos abstratos, enquanto as implementações
concretas irão personalizar os processos para as necessidades do sistema. Diversos padrões podem ser
combinados com o Abstract Factory, como Singleton para a fábrica concreta e Template Method para o
processamento comum, mas é bom lembrar que ocorre algum acoplamento, pois alterações na parte abstrata
irão impactar em todos os elementos concretos.
 
Explore no vídeo a seguir as consequências e padrões relacionados ao Abstract Factory.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
O padrão Abstract Factory promove o encapsulamento do processo de criação de objetos, isolando os
clientes das implementações concretas, permitindo que os clientes sejam implementados pelo uso apenas de
abstrações. Além disso, esse padrão promove a consistência entre produtos relacionados, isto é, produtos da
mesma família que devem ser utilizados em conjunto. Entretanto, a introdução de novos produtos não é
simples, pois exige mudança em todas as fábricas. Cada novo produto inserido exige a adição de uma nova
operação de criação em cada fábrica da estrutura.
 
O padrão Abstract Factory está relacionado com outros padrões de criação. Cada operação de criação é
tipicamente implementada utilizando o padrão Factory Method. É possível configurar fábricas mais flexíveis
utilizando o padrão Prototype. Cada fábrica concreta pode ser definida como um Singleton, já que,
normalmente, apenas uma instância de uma fábrica específica precisa ser instanciada.
 
No exemplo apresentado anteriormente, poderíamos ainda eliminar a duplicação de código similar presente
nas operações registrarCliente e registrarConta, generalizando a fábrica para retornar objetos de um tipo
genérico Decoder (ao invés de decodificadores específicos) e transformando essas operações em objetos por
meio da aplicação de outros padrões, como o Command e o Template Method.
Recomendação
Estude os padrões e tente modificar a estrutura do exemplo, aplicando-os na nova solução. 
Para melhorar a solução inicial, o primeiro passo é a definição das interfaces Decoder e DecoderFactory, bem
como suas implementações concretas para os formatos XML e CSV, seguindo o padrão Singleton.
java
public interface Decoder {
 MsgRegistroCliente decodeCliente(String textoMsg);
 MsgRegistroConta decodeConta(String textoMsg);
}
public interface DecoderFactory {
 Decoder createDecoder();
}
public class CSVDecoder implements Decoder{
 @Override
 public MsgRegistroClientedecodeCliente(String textoMsg) {
 MsgRegistroCliente msg = new MsgRegistroCliente();
 // Lógica para tratamento da mensagem
 return msg;
 }
 @Override
 public MsgRegistroConta decodeConta(String textoMsg) {
 MsgRegistroConta msg = new MsgRegistroConta();
 // Lógica para tratamento da mensagem
 return msg;
 } 
 
}
public class CSVDecoderFactory implements DecoderFactory{
 // Singleton
 private static CSVDecoderFactory instance = new CSVDecoderFactory();
 private CSVDecoderFactory(){}
 public static CSVDecoderFactory getInstance(){
 return instance;
 } 
 
 @Override
 public Decoder createDecoder() {
 return new CSVDecoder();
 }
}
public class XMLDecoder implements Decoder{
 @Override
 public MsgRegistroCliente decodeCliente(String textoMsg) {
 MsgRegistroCliente msg = new MsgRegistroCliente();
 // Lógica para tratamento da mensagem
 return msg;
 }
 @Override
 public MsgRegistroConta decodeConta(String textoMsg) {
 MsgRegistroConta msg = new MsgRegistroConta();
 // Lógica para tratamento da mensagem
 return msg;
 }
 
}
public class XMLDecoderFactory implements DecoderFactory{
 // Singleton
 private static XMLDecoderFactory instance = new XMLDecoderFactory();
 private XMLDecoderFactory(){}
 public static XMLDecoderFactory getInstance(){
 return instance;
 } 
Como existem muitos passos comuns, o padrão Template Method pode ser utilizado, definindo o núcleo de
execução do serviço para cada formato de dados.
java
public abstract class DecoderTemplate {
 public abstract DecoderFactory getFactory();
 public void registrarCliente(String textoMsg) {
 Decoder decoder = getFactory().createDecoder();
 MsgRegistroCliente msg = decoder.decodeCliente(textoMsg);
 // Processamento da mensagem
 }
 public void registrarConta(String textoMsg) {
 Decoder decoder = getFactory().createDecoder();
 MsgRegistroConta msg = decoder.decodeConta(textoMsg);
 // Processamento da mensagem
 }
}
public class CSVDecoderTemplate extends DecoderTemplate{
 @Override
 public DecoderFactory getFactory() {
 return CSVDecoderFactory.getInstance();
 }
}
public class XMLDecoderTemplate extends DecoderTemplate{
 @Override
 public DecoderFactory getFactory() {
 return XMLDecoderFactory.getInstance();
 }
} 
Finalmente, o serviço pode ser implementado de forma simples, com estrutura similar a um Factory Method
em sua codificação.
java
public class DecoderService {
 private static DecoderTemplate getTemplate(String origem){
 return "X".equals(origem) ? new XMLDecoderTemplate() : 
 new CSVDecoderTemplate();
 }
 public void registrarCliente(String textoMsg, String origem) {
 getTemplate(origem).registrarCliente(textoMsg);
 }
 public void registrarConta(String textoMsg, String origem) {
 getTemplate(origem).registrarConta(textoMsg);
 }
} 
Atividade 3
O Abstract Factory é um padrão de criação bastante utilizado por diversas ferramentas de software, sendo
indicado no contexto de
A
um framework que define o processamento geral de persistência em banco de dados, com lacunas que
devem ser preenchidas pelo programador através de herança.
B uma biblioteca que precisa fornecer um mecanismo padronizado para gerenciamento de instâncias,
garantindo um único ponto de acesso a seus recursos.
C
um framework que define uma interface de acesso a pools de objetos e uma segunda para geração delas,
permitindo personalização pelo programador, com a definição de seus próprios objetos e a geração de
interfaces customizadas para esses objetos.
D uma game engine que precisa organizar a lógica de jogo de forma flexível, permitindo que diferentes
estratégias sejam aplicadas conforme as condições do ambiente.
E uma biblioteca de persistência que implementa um mecanismo para adaptar a conexão a diferentes
bancos de dados de acordo com parâmetros fornecidos pelo usuário.
A alternativa C está correta.
Ao definir uma interface-padrão de acesso e outra para geração das primeiras, o framework está utilizando
o padrão Abstract Factory, já que foi definida uma família de produtos e seus geradores. Quanto aos
demais, o controlador global de acesso iria utilizar Singleton; preencher lacunas em processamentos
remete ao padrão Template Method; a game engine deveria utilizar Strategy; e o método para fornecer
conexões seguiria o padrão Factory Method. 
Abstract Factory X Injeção de dependências
Injeção de dependência e inversão de controle são conceitos comuns em frameworks e frequentemente
trabalham juntos. Ao injetar uma dependência, como no uso de componentes de persistência no Spring, o
framework assume o controle dos processos, caracterizando a inversão do controle. Essa técnica tem
substituído o padrão Abstract Factory, oferecendo uma abordagem mais simples para o desenvolvimento. No
entanto, as fábricas ainda estão presentes no código interno dos frameworks, diferenciando apenas o modelo
de escrita, sem alterar a funcionalidade.
 
Acompanhe no vídeo a seguir como o framework de injeção de dependências pode substituir uma
implementação manual do padrão Abstract Factory.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Um exemplo clássico de utilização do padrão Abstract Factory é a implementação de persistência em banco
de dados segundo o padrão Data Access Object (DAO), em que a fábrica abstrata apresenta métodos para a
geração de um DAO abstrato, o qual prevê os métodos para inclusão, alteração, exclusão e consulta de
registros. Ao direcionar para um banco de dados específico, temos a fábrica concreta e o DAO concreto, os
quais especializam os elementos abstratos previstos.
Em termos arquiteturais, utilizando SQL ANSI,
boa parte do código se repete, mas para cada
banco de dados ocorrerá uma configuração
diferenciada para a conexão. Esse fato é
aproveitado pelos frameworks, como no Spring,
em que ocorre a substituição das fábricas pelo
uso de anotações, promovendo a injeção de
dependências, e deixando para o framework a
responsabilidade de executar os comandos
SQL necessários.
 
Internamente, temos uma fábrica abstrata e
arquivos para a configuração da conexão, e o
Spring utiliza as informações de conexão, além do driver utilizado, para gerar cada comando SQL
corretamente, a partir de uma interface do tipo DAO anotada como JpaRepository e uma classe de entidade
anotada como Entity. Todo o mecanismo é atrelado a um serviço quando uma instância do DAO é anotada
com @Autowired, o que configura a injeção da dependência, em que o controle dos processos invocados para
a consulta e manipulação de dados será sempre delegado para o framework.
Atividade 4
Através da injeção de dependência é possível inverter o controle dos processos, delegando para o framework
a responsabilidade sobre a execução de determinados passos. Em muitos casos ela pode substituir o padrão
Abstract Factory, e uma vantagem obtida com o uso da injeção de dependências é a
A diminuição do acoplamento.
B definição formal de interfaces para os produtos.
C utilização direta do padrão Singleton.
D independência de configurações externas.
E flexibilidade maior para o programador.
A alternativa A está correta.
O Abstract Factory apresenta alto acoplamento, pois qualquer alteração nas interfaces base impacta todos
os códigos que as utilizam, diferentemente da injeção de dependência, que geralmente opera com
anotações. No Abstract Factory, a definição formal de interfaces é essencial, permitindo ainda o uso de
Singleton nas fábricas concretas, algo que não ocorre na injeção de dependência, que trabalha com
inserções pontuais. Além disso, a injeção de dependências geralmente exige alguma configuração externa,
como os arquivos do Spring,que especificam conexões com bancos de dados e elementos de segurança.
Por fim, a injeção de dependências segue um modelo rígido, com anotações bem específicas, enquanto o
Abstract Factory exige programação interna, proporcionando maior flexibilidade.
3. Padrão de projeto Builder
Intenção e problema do padrão Builder
Em muitas situações, a construção de um objeto complexo pode levar à definição de múltiplos construtores
sobrecarregados, ou à utilização de diversos parâmetros que acabam recebendo valor nulo, uma situação
comum em diversas classes Java. 
 
O padrão Builder permite desacoplar os objetos gerados dos passos para a construção, para que estes
passos possam ser combinados de diferentes formas, simplificando a configuração de objetos complexos.
Nota-se cada vez mais a presença do Builder em diversas bibliotecas. A Retrofit, por exemplo, permite gerar
conexões HTTP com diferentes configurações, incluindo elementos de autenticação, quando presentes, e
interpretação de dados em formatos como JSON e XML.
 
Entenda no vídeo a seguir a intenção e o problema do padrão Builder.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
Intenção do padrão Builder
Builder é um padrão que visa separar a construção de um objeto complexo de sua representação, de forma
que o mesmo processo de construção possa construir diferentes representações desse objeto.
Problema do padrão Builder
Suponha que você esteja fazendo um sistema para uma corretora de valores mobiliários, e que esse sistema
permita que o cliente exporte suas notas de negociação em diferentes formatos, tais como: XML, PDF ou XLS.
 
Imagine que o processo de construção de qualquer representação da nota de negociação seja definido por
três passos fundamentais:
Construir
Idealizar o cabeçalho da nota.
Listar
Enumerar as operações da nota.
Gerar
Criar o sumário com os totais e taxas de todas as operações do dia.
Uma solução frequentemente utilizada para tal problema é definir todas as possíveis conversões em uma
única classe, como ilustrado no código a seguir.
java
public class ExportadorNota {
 public byte[] exportarNota(NotaNegociacao nota, String formato) {
 if (“XML”.equals(formato))
 return gerarNotaXML(nota);
 else if (“PDF”.equals(formato))
 return gerarNotaPDF(nota);
 else if (“XLS”.equals(formato))
 return gerarNotaXLS(nota);
 }
 
 private byte[] gerarNotaXML(NotaNegociacao nota) {
 // construir cabeçalho em XML
 // listar os itens da nota em XML
 // gerar sumário em XML
 // retornar conteúdo da nota no formato XML
 }
 
 private byte[] gerarNotaPDF(NotaNegociacao nota) {
 // construir cabeçalho em PDF
 // listar os itens da nota em PDF
 // gerar sumário em PDF
 // retornar conteúdo da nota no formato PDF
 }
 
 private byte[] gerarNotaXLS(NotaNegociacao nota) {
 // construir cabeçalho em XLS
 // listar os itens da nota em XLS
 // gerar sumário em XLS
 // retornar conteúdo da nota no formato XLS
 }
 
 } 
A operação exportarNota recebe a nota de negociação a ser exportada e o formato de exportação (XML, PDF
ou XLS).
 
A solução apresentada não é adequada, pois, além de concentrar em um único módulo todas as possíveis
representações de exportação da nota de negociação, o algoritmo de construção é repetido em cada formato
específico. Além disso, o módulo deve ser modificado a cada nova forma de representação que for necessária
para a nota, violando o princípio Open Closed, um dos princípios SOLID.
Atividade 1
Assinale a alternativa que expressa a intenção do padrão de projeto Builder.
A
Permitir que um único processo de construção seja capaz de construir diferentes representações de
objetos complexos, separando, em classes distintas, o processo de construção de suas diferentes
representações.
BPermitir que diferentes processos de construção possam criar a mesma representação de um objeto
complexo, criando uma interface genérica de criação e implementando-a em diferentes subclasses.
C Permitir a instanciação de objetos complexos por meio da clonagem de objetos já existentes.
D Garantir que um objeto complexo possa ser instanciado apenas uma vez, fornecendo um ponto de
acesso único para ele.
E Permitir a instanciação de uma família de objetos complexos relacionados, garantindo que eles sejam
sempre utilizados dentro da mesma família.
A alternativa A está correta.
O objetivo do padrão Builder é permitir a reutilização de um único processo para a criação de diferentes
representações de objetos complexos. O padrão Builder não utiliza clonagem, que é uma solução
característica do padrão Prototype. O propósito do padrão Singleton não é aplicável apenas a objetos
complexos, e o propósito do padrão Abstract Factory não é aplicável apenas a famílias de objetos
complexos.
Solução do padrão Builder
O padrão Builder utiliza uma interface de construção capaz de gerar as diferentes partes de um objeto
complexo, permitindo várias implementações de construtores, para satisfazer diferentes cenários. Os métodos
para a construção das partes podem ser combinados e um método para obtenção do resultado é invocado ao
final. Por exemplo, a criação de janelas de alerta no Android segue o padrão Builder, por meio da classe
AlertDialog.Builder, trazendo métodos para definir título, texto interno e configuração de botões, entre outros
elementos, e com a geração da janela configurada ocorrendo apenas ao final, através do método create.
 
Compreenda no vídeo a seguir a solução do padrão Builder.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
A solução proposta pelo padrão Builder consiste em separar a criação de objetos complexos de quem
demanda esses objetos, conforme a estrutura definida no diagrama de classes a seguir.
Estrutura de objetos complexos separados de quem demanda esses objetos.
A interface Builder define as operações que criam as diferentes partes de um produto. Cada forma particular
de criação desse produto é definida em uma classe ConcreteBuilder, que implementa as operações
específicas para a criação das partes definidas na interface Builder.
 
O participante Director corresponde à classe que constrói o produto utilizando a interface Builder. Nessa
solução, a classe Director fica isolada do conhecimento sobre as diferentes formas de representação do
produto a ser construído.
 
O diagrama de sequência a seguir ilustra a colaboração entre os participantes do padrão Builder.
Esquema de colaboração entre os participantes do padrão Builder.
Nessa colaboração, o elemento Client representa o objeto que solicita a criação de um produto para o
Director. Para isso, ele cria o Builder específico para o produto desejado (ConcreteBuilder), injetando-o na
instanciação da classe Director.
 
A partir daí, o objeto Director é responsável por criar as diferentes partes do produto, chamando as operações
específicas do ConcreteBuilder (buildPart_1, buildPart_2 etc.). 
 
Acompanhe a seguir!
O próximo diagrama apresenta a aplicação do padrão Builder no problema apresentado. A classe 
ExportadorNota corresponde ao participante Director na estrutura definida pelo padrão. NotaBuilder
representa a interface Builder, enquanto NotaPDFBuilder e NotaXLSBuilder correspondem ao participante
ConcreteBuilder. Cada builder específico constrói uma representação específica do produto (NotaPDF e 
NotaXLS).
Adição 
O Builder concreto adiciona as partes
solicitadas pelo Director, pois somente ele
conhece os detalhes da representação do
produto.
Pedido 
Ao final, o elemento Client pede o
produto construído para o
ConcreteBuilder por meio da operação
GetResult.
Aplicação do padrão Builder.
O código a seguir ilustra a estruturada implementação da solução, utilizando o padrão Builder.
 
A classe ExportadorNota recebe um builder em seu construtor. Esse builder é utilizado no método
exportarNota para gerar as partes que compõem uma nota exportada tanto em PDF quanto em XLS, isto é, o
cabeçalho, os itens negociados e o sumário.
java
public class ExportadorNota {
 private NotaBuilder builder;
 public ExportadorNotaNegociacao(NotaBuilder builder) {
 this.builder = builder;
 }
 public void exportarNota(NotaNegociacao nota) {
 builder.gerarCabecalho(nota);
 builder.gerarItensNota(nota);
 builder.gerarSumario(nota);
 }
 } 
A classe ComandoExportarNotaPDF é um exemplo de cliente do exportador de nota. O método executar
instancia um builder concreto (NotaPDFBuilder), cria um diretor (ExportadorNota), passando o builder a ser
utilizado, e chama a operação de construção do produto desejado (exportarNota). O último passo é solicitar
ao builder concreto o objeto NotaPDF construído.
java
public class ComandoExportarNotaPDF {
 public NotaPDF executar(NotaNegociacao nota) {
 NotaPDFBuilder builder = new NotaPDFBuilder();
 ExportadorNota diretor = new ExportadorNota (builder);
 diretor.exportarNota(nota);
 return builder.obterNotaPDF();
 }
 } 
Note que, nessa solução, o algoritmo geral de exportação é definido apenas na classe ExportadorNota. Além
disso, a estrutura condicional baseada no formato desejado, presente na solução original, não é mais
necessária. Dessa forma, novas representações de exportação da nota de negociação podem ser adicionadas
ao sistema, bastando adicionar novos builders e produtos correspondentes.
 
Existem algumas questões importantes na implementação desse padrão. Vejamos!
1
Concessão de acesso
Se o objeto Builder deve dar acesso apenas ao produto pronto, isto é, após a realização de todas as
etapas de construção, ou se ele pode dar acesso às partes intermediárias já construídas do produto.
 
2
Acesso ao produto
No exemplo, como o objeto que desempenha o papel de Director não precisa acessar as partes em
seu algoritmo de construção, cada Builder concreto fornece acesso apenas ao produto construído
por meio das operações obterNotaPDF e obterNotaXLS. Entretanto, caso necessário, é admissível
que as operações de construção (buildPart_1, buildPart_2 etc.) retornem uma parte intermediária do
produto.
 
3
Estrutura dos produtos
Outra questão é se os produtos devem ser estruturados em uma hierarquia. No exemplo, as classes
NotaPDF e NotaXLS não foram definidas com uma superclasse comum, pois assumimos que elas
seriam utilizadas de forma bem específica. Entretanto, nada impede que elas sejam derivadas de
uma superclasse ou implementem uma interface genérica.
 
Atividade 2
Em relação à estrutura de solução proposta pelo padrão de projeto Builder, assinale a alternativa correta:
A O participante Director é responsável pela instanciação do participante Builder.
B Somente o participante Director conhece a representação interna do produto a ser construído.
C O participante Builder conhece todas as possíveis representações internas dos produtos que podem
ser construídos.
D Tanto o participante Director quanto o participante Concrete Builder conhecem a representação interna
do produto construído.
E
Somente o participante Concrete Builder conhece a representação interna do produto que ele é
responsável por construir.
A alternativa E está correta.
O participante Director recebe um Builder como parâmetro em seu construtor, não devendo instanciar um
Builder diretamente, pois isso o faria ser dependente de todas as possíveis formas de construção de
produtos, isto é, de todos os builders concretos. O propósito do padrão é justamente esconder os detalhes
da representação interna do produto em seu respectivo builder concreto. Cada builder concreto deve
conhecer a representação interna somente do produto que ele é responsável por construir, e não de todas
as suas possíveis representações.
Consequências e padrões relacionados ao Builder
O padrão Builder gerencia construtores individuais para partes de um objeto complexo, proporcionando
flexibilidade na configuração. Diferentemente do Abstract Factory, que define construções fixas, o Builder
permite combinar construtores em qualquer ordem e reutilizá-los. Uma abordagem interessante é integrar o
padrão Command para cada construtor e usar Composite para gerenciar as partes geradas, semelhante a uma
empreiteira construindo um prédio com portaria, andares comuns e cobertura.
 
Explore no vídeo a seguir as consequências e padrões relacionados ao Builder.
Conteúdo interativo
Acesse a versão digital para assistir ao vídeo.
O padrão Builder é aplicável na construção de objetos complexos e que possam ter diferentes representações
internas. Encapsulando o conhecimento dessas representações em builders concretos que implementam uma
interface genérica comum, os clientes ficam isolados da forma como esses objetos são internamente
construídos.
 
O padrão Abstract Factory, assim como o Builder, pode construir objetos complexos. A diferença principal
entre os dois padrões é que:
O padrão Composite é utilizado para representar objetos compostos por outros em uma hierarquia de
especializações de um mesmo elemento comum, como ocorre, por exemplo, em uma estrutura de diretórios e
arquivos. O padrão Builder pode ser utilizado para implementar a construção de objetos com uma estrutura de
composição complexa resultante da utilização do padrão Composite.
Aplicações do padrão de projeto Builder
Builder 
Oferece um mecanismo de construção de
um objeto complexo em etapas.
Abstract Factory 
Seu foco é definir famílias de produtos.
Um produto da família é retornado
com apenas uma chamada de
operação.
Uma aplicação típica do padrão Builder é a substituição de múltiplos construtores, permitindo a configuração
apenas das características de interesse antes da geração do objeto complexo, uma estratégia utilizada, por
exemplo, pela biblioteca Retrofit, que permite criar um cliente para serviços do tipo REST com poucas linhas
de código.
java
public class Pessoa {
 public Long idPessoa;
 public String nome;
 public Integer idade;
}
public interface PessoaService {
 @GET("/pessoas")
 public Call> obterPessoas();
}
public class PessoCliente {
 public static void main( String args[ ] ) throws Exception {
 // Uso do padrão Builder
 Retrofit retrofit = new Retrofit.Builder() 
 .baseUrl("http://meuservidor/") 
 .addConverterFactory(JacksonConverterFactory.create()) 
 .build();
 PessoaService service = retrofit.create(PessoaService.class);
 Service.obterPessoas().execute().body().forEach( p -> {
 System.out.println(p.nome);
 });
 }
} 
Também podemos criar nosso próprio Builder, como no exemplo seguinte, que define um sistema simples,
ilustrando o gerenciamento de construções prediais. Inicialmente é definido um enumerado e uma classe
Construção, que pode ser portaria, cobertura ou andar comum.
java
public enum TipoConstrucao {
 CT_PORTARIA, CT_ANDAR_COMUM, CT_COBERTURA;
}
public class Construcao {
 
 public int apartamentos = 0;
 public final TipoConstrucao tipo;
 public Construcao(TipoConstrucao tipo) {
 this.tipo = tipo;
 }
} 
Em seguida é necessário definir o Builder para cada tipo de construção a partir de uma classe abstrata
ConstrucaoBuilder, que define apenas uma restrição, segundo a qual apenas andares comuns podem ter
apartamentos. Observe o retorno do próprio Builder nos setters, permitindo chamadas encadeadas para
configuração.
java
public abstract class ConstrucaoBuilder {
 
 private int apartamentos = 0;
 protected abstract TipoConstrucao getTipo();
 public ConstrucaoBuilder setApartamentos(int apartamentos){if(getTipo()==TipoConstrucao.CT_ANDAR_COMUM)
 this.apartamentos = apartamentos;
 return this;
 }
 
 public Construcao getResultado(){
 Construcao construcao = new Construcao(getTipo());
 construcao.apartamentos = apartamentos;
 return construcao;
 }
 
}
public class ConstrucaoAndarBuilder extends ConstrucaoBuilder{
 @Override
 protected TipoConstrucao getTipo() {
 return TipoConstrucao.CT_ANDAR_COMUM;
 }
 
}
public class ConstrucaoCoberturaBuilder extends ConstrucaoBuilder{
 @Override
 protected TipoConstrucao getTipo() {
 return TipoConstrucao.CT_COBERTURA;
 }
 
}
public class ConstrucaoPortariaBuilder extends ConstrucaoBuilder{
 @Override
 protected TipoConstrucao getTipo() {
 return TipoConstrucao.CT_PORTARIA;
 }
 
} 
Com base em construções comuns, é possível definir a classe Predio e o Builder mais apropriado para a
geração dos objetos. Note que a cobertura é opcional, além do número de andares e a existência de uma lista
para armazenar as construções.
java
public class Predio {
 public String endereco = "";
 public int andares_comuns = 1;
 public boolean cobertura = false;
 public ArrayList andares = new ArrayList(); 
}
public class PredioBuilder {
 
 private String endereco = "";
 private int andares_comuns = 1;
 private boolean cobertura = false;
 
 public PredioBuilder setEndereco(String endereco){
 this.endereco = endereco;
 return this;
 }
 
 public PredioBuilder setAndaresComuns(int andares_comuns){
 this.andares_comuns = andares_comuns;
 return this;
 }
 
 public PredioBuilder setCobertura(boolean cobertura){
 this.cobertura = cobertura;
 return this;
 }
 
 public Predio getResultado(){
 Predio predio = new Predio();
 predio.cobertura = cobertura;
 predio.endereco = endereco;
 predio.andares_comuns = andares_comuns;
 predio.andares.add(new ConstrucaoPortariaBuilder().getResultado());
 for(int i=0; i 
Por fim, é possível testar a construção do prédio, utilizando as configurações definidas na classe 
PredioBuilder.
java
public class ExemploBuilder {
 public static void main(String[] args) {
 Predio predio = new PredioBuilder().setCobertura(true)
 .setAndaresComuns(7)
 .setEndereco("Rua das Marrecas, 1313")
 .getResultado();
 System.out.println(predio.endereco);
 }
 
} 
Atividade 3
O padrão de projeto Builder oferece uma solução interessante para a construção de objetos complexos, 
sendo adotado na biblioteca Retrofit e construção de diálogos do Android, além de muitos outros cenários. 
Indique o contexto a seguir em que o padrão Builder seria adequado.
 A 
Processamento de uma requisição por uma sequência de tratadores específicos, 
em que em cada um ocorre o tratamento pontual e o envio para o tratador seguinte.
 
 B 
Geração de objetos de controle para diferentes tipos de drone, com diversas 
configurações alternativas, disponibilizadas por cada fabricante.
 
 C 
Especificação de gestor de conexões para mensagerias, em que é recebido o tipo e 
o endereço em um método, para receber o objeto correto e já configurado para 
acesso.
 
 D 
Definição de modelos preexistentes, com funcionalidades básicas, que podem 
ser copiados e modificados para satisfazer aos interesses do sistema.
 
 E 
Gerenciamento dos objetos existentes em um pool de 
conexões.
 
A alternativa B está correta.
O padrão Builder é utilizado para permitir diferentes configurações em objetos complexos, como no 
controle de drones. Quanto aos demais, para o processamento sequencial de requisições é utilizado Chain 
of Responsibility; um método para obtenção de conexões iria utilizar Factory Method; modelos 
preexistentes remetem ao padrão Prototype; e o padrão Flyweight é voltado para o gerenciamento de 
pools de objetos.
 
 
4. Padrões de projeto Prototype e Singleton
 
 
Intenção e problema do padrão Prototype
Ao instanciar um objeto, é comum precisar configurar todos os seus atributos, muitas vezes repetindo 
configurações de objetos já criados. O padrão Prototype resolve isso ao definir um protótipo que armazena 
configurações comuns, permitindo que novos objetos herdem essas definições sem repetir o processo de 
inicialização. Apenas as modificações específicas são aplicadas conforme o contexto. Esse conceito se 
assemelha aos protótipos do mundo real, que podem ser usados diretamente ou servir de base para novos 
produtos.
 
Entenda no vídeo a seguir a intenção e o problema do padrão Prototype.
 
 
 
 
 
 
 
Conteúdo interativo
 
Acesse a versão digital para assistir ao vídeo.
 
 
 
 
Intenção do padrão Prototype
O padrão Prototype permite a instanciação de objetos a partir da geração de uma cópia de um objeto 
protótipo, fazendo com que o módulo cliente não precise conhecer a classe específica que está sendo 
instanciada.
Problema do padrão Prototype
Suponha que, no problema apresentado no padrão Abstract Factory, os decodificadores da mensagem 
Registrar cliente, ao invés de criarem apenas objetos da classe MsgRegistrarCliente, tivessem de criar objetos 
de classes específicas conforme a origem da mensagem.
 
Isso significa que a classe RegistrarClienteXMLDecoder, por exemplo, ao invés de criar uma instância de 
MsgRegistrarCliente, teria de criar uma instância de MsgRegistrarCliente_X, MsgRegistrarCliente_Y ou MsgReg
istrarCliente_Z, dependendo da organização origem da mensagem, imaginando que a validação de cada 
registro de cliente variasse conforme a organização.
 
O diagrama a seguir ilustra essa solução. Definimos uma especialização de MsgRegistrarCliente para cada 
origem (veja os sufixos X, Y e Z definidos nas subclasses). Cada subclasse implementa um método específico 
de validação da mensagem.
Métodos de validação das mensagens.
Você consegue perceber que essa solução adiciona complexidade ao decodificador?
 
Veja, no código a seguir, como a classe RegistrarClienteXMLDecoder fica mais complexa, uma vez que tem de 
conhecer cada subclasse de MsgRegistrarCliente.
java
public class RegistrarClienteXMLDecoder {
 public MsgRegistrarCliente decode(String textoMsg, String origem) {
 MsgRegistrarCliente msg;
 if (“X”.equals(origem)) {
 msg = new MsgRegistrarCliente_X();
 else if (“Y”.equals(origem)) {
 msg = new MsgRegistrarCliente_Y();
 else if (“Z”.equals(origem)) {
 msg = new MsgRegistrarCliente_Z();
 }
 // … aqui viria o código de decodificação e preenchimento dos atributos
 // do objeto MsgRegistrarCliente
 return msg;
 }
 } 
Inserir o processo de decisão sobre o objeto a ser instanciado na implementação do método de 
decodificação da mensagem, além de adicionar complexidade, torna a implementação inflexível à adição de 
novas origens, pois teríamos de modificar o código inserindo novos comandos condicionais, o que é uma 
clara violação do princípio Open Closed, um dos princípios SOLID.
Atividade 1
Assinale a alternativa que expressa a intenção do padrão de projeto Prototype.
 A 
Permitir a instanciação de um objeto a partir de uma operação de 
clonagem fornecida por um objeto já existente.
 
 B 
Permitir a criação