3.1. Módulo Padrões de Projeto GoFs Criacionais
Padrões Criacionais e a Responsabilidade de Instanciação
Ao modelar um diagrama de classes, um dos desafios mais relevantes está em definir quem é responsável por instanciar os objetos e, em alguns casos, transferir essa responsabilidade de forma eficiente. Para isso, os padrões criacionais do Gang of Four (GoF) oferecem soluções reutilizáveis que favorecem um design mais flexível, desacoplado e de melhor desempenho. A seguir, apresentamos os principais padrões criacionais:
-
Factory Method: Permite que subclasses decidam qual classe instanciar, delegando a responsabilidade de criação. Isso evita acoplamentos diretos e melhora a escalabilidade do sistema.
-
Abstract Factory: Foca na criação de famílias de objetos relacionados, promovendo consistência em estruturas com hierarquias complexas. Ideal para sistemas com múltiplas variações de objetos interdependentes.
-
Builder: Separa a construção de um objeto da sua representação final. Útil para criar objetos complexos com diferentes combinações de atributos, mantendo a clareza e reutilização do código.
-
Prototype: Cria novos objetos a partir da clonagem de um objeto existente. Ajuda a melhorar o desempenho em cenários onde a criação do zero é custosa, como em jogos ou simulações.
-
Singleton: Garante que apenas uma instância de uma classe exista, fornecendo um ponto de acesso global. Aplicado em contextos como configurações globais e gerenciadores de recursos.
-
Multiton: Variante do Singleton que permite múltiplas instâncias únicas, identificadas por chaves específicas. Indicado para sistemas que exigem um número controlado de instâncias distintas.
-
Object Pool: Gerencia um conjunto de objetos reutilizáveis, evitando o custo de criação e destruição contínua. Muito aplicado em jogos e sistemas que exigem alta performance com muitos objetos temporários.
Esses padrões são fundamentais para lidar com a complexidade da criação de objetos em sistemas orientados a objetos, tornando o software mais fácil de manter, evoluir e otimizar ao longo do tempo.
Aplicação no Projeto
Para a utilização do padrão Singleton, foi inicialmente considerada a possibilidade de instanciar múltiplos objetos da classe RegistradorLog, como nos serviços de monitoramento e análise de quedas. No entanto, essa abordagem foi descartada, pois poderia resultar em registros inconsistentes, duplicados ou até mesmo em conflitos de concorrência durante o processo de log.
Com o Singleton, todos os eventos, sejam quedas detectadas, notificações enviadas ou erros internos seriam registrados por meio de uma única instância. Isso evitaria duplicações de log, inconsistências e problemas de concorrência que poderiam surgir caso diferentes partes do sistema tentassem gerenciar seus próprios registros de forma independente. Assim, qualquer componente do sistema que precisasse registrar um evento acessaria a instância única de RegistradorLog por meio do método estático getInstance(), garantindo a coesão do registro de eventos no sistema.
Implementação em código - Singleton
public class RegistradorLog implements ListenerQueda {
private static RegistradorLog instance;
private RegistradorLog() {
// Construtor privado para implementar o Singleton
System.out.println("[RegistradorLog] Instância do RegistradorLog criada.");
}
public static RegistradorLog getInstance() {
if (instance == null) {
instance = new RegistradorLog();
}
return instance;
}
@Override
public void onQuedaDetectada(Monitoramento m, ResultadoAnalise r) {
System.out.println("[RegistradorLog] Queda detectada e registrada no log. " + m.getResumo() + " - Risco: " + r.getRiscoQueda());
}
}
Testando o Singleton do RegistradorLog
import observer.*;
public class SistemaMonitoramentoTest {
public static void main(String[] args) {
System.out.println("--- Testando o Singleton do RegistradorLog ---");
// Primeira tentativa de obter a instância
System.out.println("\nChamando RegistradorLog.getInstance() pela primeira vez...");
RegistradorLog log1 = RegistradorLog.getInstance();
System.out.println("log1 obtido: " + log1); // Isso vai imprimir o endereço de memória do objeto
// Segunda tentativa de obter a instância
System.out.println("\nChamando RegistradorLog.getInstance() pela segunda vez...");
RegistradorLog log2 = RegistradorLog.getInstance();
System.out.println("log2 obtido: " + log2); // Deve imprimir o MESMO endereço de memória do log1
// Terceira tentativa de obter a instância
System.out.println("\nChamando RegistradorLog.getInstance() pela terceira vez...");
RegistradorLog log3 = RegistradorLog.getInstance();
System.out.println("log3 obtido: " + log3); // Também deve imprimir o MESMO endereço de memória
System.out.println("\n--- Verificando se as instâncias são as mesmas ---");
// Compara as referências para confirmar que são o mesmo objeto
boolean saoIguais1_2 = (log1 == log2);
boolean saoIguais2_3 = (log2 == log3);
boolean saoIguais1_3 = (log1 == log3);
System.out.println("log1 é o mesmo objeto que log2? " + saoIguais1_2);
System.out.println("log2 é o mesmo objeto que log3? " + saoIguais2_3);
System.out.println("log1 é o mesmo objeto que log3? " + saoIguais1_3);
System.out.println("\n--- Teste de Singleton Concluído ---");
}
}
Resultados dos Testes Singleton do RegistradorLog
Para a utilização do padrão Factory Method, foi inicialmente considerada a possibilidade de instanciar diretamente objetos da classe Notificacao (ou suas subclasses concretas, como NotificacaoSMS, NotificacaoEmail, etc.) em diversos pontos do sistema. No entanto, essa abordagem foi descartada, pois implicaria em um acoplamento forte entre o código cliente (que solicita a notificação) e as classes concretas de Notificacao. Isso tornaria o sistema rígido a mudanças, dificultando a introdução de novos tipos de notificação ou a modificação dos existentes, pois cada alteração exigiria modificações em múltiplos locais do código.
Com o Factory Method, a criação de objetos Notificacao é centralizada e delegada à NotificacaoFactory e suas subclasses concretas, como a ConcreteFactory. Isso permite que o sistema crie diferentes tipos de notificações (por exemplo, NotificacaoSMS, NotificacaoEmail, NotificacaoApp) sem que o código cliente precise saber qual classe concreta está sendo instanciada. A ConcreteFactory encapsula a lógica de criação, decidindo qual tipo de Notificacao será produzido com base nos parâmetros fornecidos (tipo, prioridade, destinatário). Assim, qualquer componente do sistema que precise enviar uma notificação simplesmente "chama" o método criarNotificacao() da fábrica.
Implementação em código - Factory Method
package factory;
import enums.Prioridade;
import enums.TipoAlerta;
import model.Notificacao;
import model.PessoaCuidador;
public abstract class NotificacaoFactory {
public abstract Notificacao criarNotificacao(TipoAlerta tipo, Prioridade prioridade, PessoaCuidador destinatario);
}
package factory;
import enums.Prioridade;
import enums.TipoAlerta;
import model.Notificacao;
import model.PessoaCuidador;
import strategy.Email;
import strategy.IEnvio;
import strategy.Push;
import strategy.SMS;
public class ConcreteFactory extends NotificacaoFactory {
@Override
public Notificacao criarNotificacao(TipoAlerta tipo, Prioridade prioridade, PessoaCuidador destinatario) {
IEnvio estrategia;
switch (prioridade) {
case ALTA:
estrategia = new SMS();
break;
case MEDIA:
estrategia = new Email();
break;
case BAIXA:
estrategia = new Push();
break;
default:
throw new IllegalArgumentException("Prioridade inválida");
}
return new Notificacao(tipo, prioridade, estrategia, destinatario);
}
}
A implementação da Factory Concreta não foi aplicado o padrão Composite, será assim mostrado na seção de sua devida implementação. Acesse aqui
Implementações das Classes
As implementações completas das classes mencionadas neste documento podem ser conferidas no repositório oficial do projeto, disponível em:
O diretório src/monitora
contém o código-fonte Java estruturado em pacotes.
Referências
REFACTORING GURU. Padrões de projeto criacionais. Disponível em: https://refactoring.guru/pt-br/design-patterns/creational-patterns. Acesso em: 30 de maio de 2025.
Histórico de Versões
Versão | Commit da Versão | Data | Descrição | Autor(es) | Revisor(es) | Descrição da Revisão | Commit da Revisão |
---|---|---|---|---|---|---|---|
1.0 | Ver Commit | 01/06/2025 | Adição do diagrama | Altino Arthur, Márcio Henrique e Daniel de Sousa | Revisor | — | — |
1.1 | Ver Commit | 01/06/2025 | Adição do diagrama factory method | Altino Arthur, Márcio Henrique e Daniel de Sousa | Revisor | — | — |
1.2 | Ver Commit | 01/06/2025 | Adição do diagrama Singleton | Altino Arthur, Márcio Henrique e Daniel de Sousa | Revisor | — | — |
1.3 | Ver Commit | 01/06/2025 | Adição da Implementação Singleton | Altino Arthur, Márcio Henrique e Daniel de Sousa | Revisor | — | — |
1.4 | Ver Commit | 01/06/2025 | Adição da Implementação Factory | Altino Arthur, Márcio Henrique e Daniel de Sousa | Revisor | — | — |