Padrão de Projeto Builder: Guia Completo para Implementação

Introdução ao Padrão Builder

O padrão Builder é um padrão de projeto criacional que permite a construção de objetos complexos passo a passo. Ele separa a construção de um objeto complexo de sua representação, permitindo que o mesmo processo de construção crie diferentes representações.

Problema a Resolver

Considere uma classe de sistema que precisa ser configurada com múltiplos parâmetros:

  • processador (obrigatório)
  • memoriaRAM (obrigatório)
  • portasUSB (opcional)
  • teclado (opcional)
  • monitor (opcional)

Enquanto dois parâmetros são obrigatórios, os outros três são opcionais.

Abordagens Tradicionais

Construtor Encadeado

Padrão JavaBean

Limitações

Abordagem 1

A principal desvantagem é a dificuldade de uso e leitura. Ao invocar um construtor de classe, é necessário primeiro decidir qual usar, pois os parâmetros são numerosos e de tipos diferentes, o que facilita a confusão.

Abordagem 2

Durante o processo de construção, o estado do objeto pode ser alterado acidentalmente, causando erros. Como as propriedades da classe são definidas em etapas separadas, é mais propenso a erros.

Características

Em desenvolvimento de software, frequentemente é necessário criar um objeto complexo que consiste em várias subpartes combinadas em etapas específicas.

Os produtos são compostos por várias partes, que podem ser selecionadas de forma flexível, mas o processo de criação é semelhante. Essa criação não pode ser descrita adequadamente pelos padrões de fábrica anteriores, e apenas o padrão Builder descreve bem a criação desse tipo de produto.

Implementação do Padrão Builder

Primeiro passo: Criar a classe de produto, no nosso caso, a classe Sistema


/**
 * Produto Sistema
 *
 */
public class Sistema {

    private final String processador;// obrigatório
    private final String memoriaRAM;// obrigatório
    private int portasUSB;// opcional
    private String teclado;// opcional
    private String monitor;// opcional

    public Sistema(String processador, String memoriaRAM) {
        this.processador = processador;
        this.memoriaRAM = memoriaRAM;
    }

    public void setPortasUSB(int portasUSB) {
        this.portasUSB = portasUSB;
    }

    public void setTeclado(String teclado) {
        this.teclado = teclado;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    @Override
    public String toString() {
        return "Sistema{" + "processador='" + processador + '\'' + ", memoriaRAM='" + memoriaRAM + '\'' + 
               ", portasUSB=" + portasUSB + ", teclado='" + teclado + '\'' + ", monitor='" + monitor + '\'' + '}';
    }
}

Segundo passo: Criar a classe construtora abstrata

public abstract class SistemaBuilder {
    public abstract void definirPortasUSB(int portasUSB);
    public abstract void definirTeclado(String teclado);
    public abstract void definirMonitor(String monitor);

    public abstract Sistema construirSistema();
}

Terceiro passo: Criar a classe construtora concreta

public class SistemaCompletoBuilder extends SistemaBuilder{
    private Sistema sistema;
    
    public SistemaCompletoBuilder(String processador, String memoriaRAM) {
        sistema = new Sistema(processador, memoriaRAM);
    }
    
    @Override
    public void definirPortasUSB(int portasUSB) {
        sistema.setPortasUSB(portasUSB);
    }

    @Override
    public void definirTeclado(String teclado) {
        sistema.setTeclado(teclado);
    }

    @Override
    public void definirMonitor(String monitor) {
        sistema.setMonitor(monitor);
    }

    @Override
    public Sistema construirSistema() {
        return sistema;
    }
}

Quarto passo: Criar a classe diretora

public class ConstrutorSistema {
    private SistemaBuilder builder;
    
    public void setBuilder(SistemaBuilder builder) {
        this.builder = builder;
    }

    public Sistema montarSistema(int portasUSB, String monitor, String teclado){
       builder.definirPortasUSB(portasUSB);
       builder.definirMonitor(monitor);
       builder.definirTeclado(teclado);
       return builder.construirSistema();
    }
}

Definição

O padrão Builder é um padrão de projeto que separa a construção de um objeto complexo de sua representação, permitindo que o mesmo processo de construção crie diferentes representações.

Ele divide um objeto complexo em vários objetos simples e os constrói passo a passo. Ou seja, as partes componentes do produto são fixas, mas cada parte pode ser selecionada de forma flexível.

Diferenças entre Padrão Builder e Padrão Fábrica

Padrão Fábrica

  • O padrão Fábrica concentra-se na criação de produtos
  • Os produtos criados pelo padrão Fábrica são todos idênticos
  • O foco do padrão Fábrica é apenas criar o objeto

Padrão Builder

  • O padrão Builder concentra-se na ordem das chamadas de método
  • O padrão Builder pode criar produtos complexos compostos por várias partes complexas
  • O padrão Builder não apenas cria o produto, mas também sabe de quais partes o produto é composto

Componentes do Padrão Builder

  • Produto (Product): Um objeto de produto específico.
  • Construtor Abstrato (Builder): Uma interface/abstração que especifica as interfaces para criar os vários componentes de um objeto Produto.
  • Construtor Concreto (ConcreteBuilder): Implementa a interface, construindo e montando os vários componentes.
  • Diretor (Director): Um objeto que usa a interface Builder.
  • É principalmente usado para criar um objeto complexo.
  • Funções
  • Isola o cliente do processo de produção do objeto
  • Controla o processo de produção do objeto Produto.

Vantagens do Padrão Builder

  • Ele isola a construção específica e o método de montagem, tornnado o processo de construção separado da implementação concreta.
  • Ele suporta a construção de objetos complexos e pode controlar a ordem de construção de objetos complexos.
  • Permite que o usuário altere independentemente a representação interna de um objeto.
  • Fornece um método de construção flexível que pode criar objetos quando o usuário não está seguro sobre os detalhes finais do produto.
  • Pode evitar eficazmente que o usuário danifique a estrutura do produto durante o processo de construção.

Em termos simples:

  • Encapsulamento eficaz, construção e representação separadas.
  • Boa extensibilidade, os construtores específicos são independentes uns dos outros, favorecendo o desacoplamento do sistema.
  • O cliente não precisa saber os detalhes da composição interna do produto, o construtor pode refinar gradualmente o processo de criação sem afetar outros módulos, facilitando o controle de riscos detalhados.

Desvantagens do Padrão Builder

  • Gera objetos Builder excessivos
  • Se a estrutura interna do produto mudar, todos os construtores precisam ser modificados, o que tem um custo maior

Segunda Implementação do Padrão Builder

Para resolver o problema da criação de objetos Builder excessivos, podemos usar uma abordagem alternativa.

Na classe ConfiguracaoMaquina, criamos uma classe interna estática Builder, e copiamos todos os parâmetros da classe ConfiguracaoMaquina para a classe Builder.

Exemplo:


/**
 * Construção de dados usando classe interna estática
 *
 */
public class ConfiguracaoMaquina {
    private final String processador;// obrigatório
    private final String memoriaRAM;// obrigatório
    private final int portasUSB;// opcional
    private final String teclado;// opcional
    private final String monitor;// opcional

    @Override
    public String toString() {
        return "ConfiguracaoMaquina{" + "processador='" + processador + '\'' + 
               ", memoriaRAM='" + memoriaRAM + '\'' + ", portasUSB=" + portasUSB + 
               ", teclado='" + teclado + '\'' + ", monitor='" + monitor + '\'' + '}';
    }

    private ConfiguracaoMaquina(Builder builder) {
        this.processador = builder.processador;
        this.memoriaRAM = builder.memoriaRAM;
        this.portasUSB = builder.portasUSB;
        this.teclado = builder.teclado;
        this.monitor = builder.monitor;
    }

    public static class Builder {
        private final String processador;// obrigatório
        private final String memoriaRAM;// obrigatório
        private int portasUSB;// opcional
        private String teclado;// opcional
        private String monitor;// opcional

        public Builder(String processador, String memoriaRAM) {
            this.processador = processador;
            this.memoriaRAM = memoriaRAM;
        }

        public Builder comPortasUSB(int portasUSB) {
            this.portasUSB = portasUSB;
            return this;
        }

        public Builder comTeclado(String teclado) {
            this.teclado = teclado;
            return this;
        }

        public Builder comMonitor(String monitor) {
            this.monitor = monitor;
            return this;
        }

        public ConfiguracaoMaquina construir() {
            return new ConfiguracaoMaquina(this);
        }
    }
}

Casos de Uso

Quando um objeto tem uma estrutura interna muito complexa (muitas propriedades) E você deseja separar a criação de objetos complexos de seu uso

Resumo

O padrão Builder é usado para criar objetos compostos complexos

Análise de Código Fonte

StringBuilder

  • A classe StringBuilder do JDK fornece o método append(), que é uma forma de criar objetos de forma encadeada. Ele abre o processo de construção e, ao final, chama o método toString() para obter um objeto completo.

SqlSessionFactoryBuilder

  • A classe SqlSessionFactoryBuilder no MyBatis utiliza o padrão Builder. No MyBatis, o SqlSessionFactory é produzido pelo SqlSessionFactoryBuilder.
  • A classe XMLConfigBuilder é responsável pela criação e montagem de cada componente do Configuration. Todo o processo de montagem é o seguinte:
  • XMLConfigBuilder é responsável por criar o objeto complexo Configuration, na verdade, ele representa o papel de construtor específico.
  • SqlSessionFactoryBuilder apenas encapsula a construção da instância SqlSessionFactory, simplificando o processo de construção, o que é essencialmente o padrão Builder.

Tags: builder-pattern design-patterns java software-architecture creational-patterns

Publicado em 6-21 03:46