Padrões de Projeto no JDK: Aplicações Flexíveis no Código Fonte

Neste artigo, analisamos como padrões de projeto clássicos são implementados de forma prática no Java Development Kit (JDK), demonstrando como a flexibilidade na aplicação desses padrões resolve problemas reais no desenvolvimento de software.

Padrão Fábrica na Classe Calendar

Embora muitas classes de fábrica sejam nomeadas com o sufixo "Factory", no JDK a abordagem é mais integrada. A classe java.util.Calendar exemplifica isso ao combinar funcionalidades de negócios com um método fábrica estático. O método getInstance() atua como fábrica, criando instâncias de subclasses específicas com base em parâmetros como fuso horário e localidade, sem expor os detalhes da criação ao cliente.

public abstract class Calendario implements Serializable, Cloneable, Comparable<Calendario> {
    // ... código omitido para brevidade ...

    public static Calendario obterInstancia(FusoHorario zona, Localidade local) {
        return fabricarCalendario(zona, local);
    }

    private static Calendario fabricarCalendario(FusoHorario zona, Localidade local) {
        // Lógica de criação baseada em locale e fuso horário
        if (local.obterTipoUnicode("ca") != null) {
            String tipo = local.obterTipoUnicode("ca");
            if ("budista".equals(tipo)) {
                return new CalendarioBudista(zona, local);
            } else if ("japones".equals(tipo)) {
                return new CalendarioImperialJapones(zona, local);
            }
        }
        // Fallback para calendário gregoriano
        return new CalendarioGregoriano(zona, local);
    }
}

Padrão Construtor na Classe Calendar

A mesma classe Calendar também emprega o padrão Construtor através de uma classe interna estática Builder. Enquanto o método fábrica cria objetos diferentes, o Construtor permite a construção passo a passo de um único tipo complexo com múltiplas opções configuráveis. O processo de construção no método build() combina inicialmente lógica similar à fábrica para selecionar a subclasse, seguida pela configuração detalhada dos campos.

public class Calendario {
    // ... outras partes da classe ...

    public static class Construtor {
        private long instante;
        private int[] campos;
        private String tipo;
        private FusoHorario zona;
        private boolean flexivel = true;
        private Localidade local;

        public Construtor definirInstante(long instante) {
            this.instante = instante;
            return this;
        }

        // Outros métodos de definição omitidos

        public Calendario construir() {
            if (local == null) local = Localidade.padrao();
            if (zona == null) zona = FusoHorario.padrao();

            Calendario cal;
            if (tipo == null) tipo = local.obterTipoUnicode("ca");
            
            // Seleção da subclasse baseada no tipo
            switch (tipo) {
                case "gregoriano":
                    cal = new CalendarioGregoriano(zona, local, true);
                    break;
                case "budista":
                    cal = new CalendarioBudista(zona, local);
                    break;
                default:
                    cal = new CalendarioGregoriano(zona, local, true);
            }
            // Configuração adicional do objeto
            cal.definirFlexivel(flexivel);
            // ... lógica de configuração de campos ...
            return cal;
        }
    }
}

A coexistência de fábrica e construtor na mesma classe ilustra que os padrões não são regras rígidas. A escolha depende das necessidades específicas: criar objetos de tipos variados versus construir um objeto complexo com configurações detalhadas.

Padrão Decorador na Classe Collections

A classe utilitária Collections aplica o padrão Decorador para adicionar funcionalidades a coleções exsitentes. Por exemplo, o método estático colecaoNaoModificavel() retorna uma instância decorada que restringe operações de modificação. A classe decoradora encapsula uma coleção subjacente, delegando a maioria das operações, mas lançando exceções em métodos como adicionar().

public class Colecoes {
    public static <E> Colecao<E> colecaoNaoModificavel(Colecao<? extends E> colecaoOriginal) {
        return new ColecaoDecorada<>(colecaoOriginal);
    }

    static class ColecaoDecorada<E> implements Colecao<E>, Serializable {
        private final Colecao<? extends E> colecaoBase;

        ColecaoDecorada(Colecao<? extends E> base) {
            if (base == null) throw new NullPointerException();
            this.colecaoBase = base;
        }

        public int tamanho() { return colecaoBase.tamanho(); }
        public boolean contem(Object obj) { return colecaoBase.contem(obj); }
        // Métodos de leitura delegados

        public boolean adicionar(E elemento) {
            throw new UnsupportedOperationException();
        }
        // Métodos de modificação bloqueados
    }
}

Esta implementação não é uma simples herança, mas um envolvimento que altera o comportamento, caracterizando um decorador.

Padrão Adaptador na Classe Collections

Para manter compatibilidade com versões antigas, o JDK usa o padrão Adaptador. A interface antiga Enumeracao é adaptada para funcionar com o novo iterador Iterador. O método estático enumeracao() na classe Collections retorna uma classe anônima que implementa Enumeracao delegando as chamadas para um Iterador.

public interface Iterador<E> {
    boolean temProximo();
    E proximo();
}

public class Colecoes {
    public static <E> Enumeracao<E> enumeracao(final Colecao<E> colecao) {
        return new Enumeracao<E>() {
            private final Iterador<E> iterador = colecao.iterador();

            public boolean temMaisElementos() {
                return iterador.temProximo();
            }

            public E proximoElemento() {
                return iterador.proximo();
            }
        };
    }
}

Este adaptador permite que código legado continue operando sem modificações, enquanto o JDK evolui internamente.

A análise do JDK revela que a aplicação de padrões de projeto frequentemente envolve adaptações criativas. Em vez de seguir implementações canônicas, os desenvolvedores combinam e modificam padrões para atender a requisitos específicos, como desempenho, compatibilidade ou clareza de código. Essa flexibilidade é essencial para soluções de software eficazes no mundo real.

Tags: java design patterns JDK Factory Pattern Builder Pattern

Publicado em 6-12 21:03 por Thomas