Pular para conteúdo

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

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


Singleton

Autores: Altino Arthur, Márcio Henrique e Daniel de Sousa


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());
    }
}

Autores: Altino Arthur, Márcio Henrique e Daniel de Sousa


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 ---");
    }
}
Autores: Altino Arthur, Márcio Henrique e Daniel de Sousa

Resultados dos Testes Singleton do RegistradorLog

Testes Singleton

Autores: Altino Arthur, Márcio Henrique e Daniel de Sousa



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.


Factory

Autores: Altino Arthur, Márcio Henrique e Daniel de Sousa


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:

https://github.com/UnBArqDsw2025-1-Turma01/2025.1-T01-_G1_Embarcado_Entrega_03/tree/main/src/monitora

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