Lista de Exercícios 003: Builder
Exercício 1: “É caso para Builder ou não é?”
Seção intitulada “Exercício 1: “É caso para Builder ou não é?””Para cada cenário abaixo, decida se faz sentido usar Builder ou se não é apropriado, e justifique em 2–3 frases.
- Criação de um objeto
Enderecocom campos obrigatórios (rua,cidade,cep) e apenas um campo opcional (complemento). - Montagem de uma requisição HTTP complexa (por exemplo, com biblioteca como OkHttp), com headers opcionais, corpo em diferentes formatos (JSON, form-data), autenticação, cache, timeouts.
- Criação de objetos de configuração para um cliente de banco de dados (
url,usuario,senha,poolSize,ssl,timeout,retryPolicy, etc.), com muitas combinações de parâmetros opcionais. - Instanciação de uma classe
Ponto2D(x, y)simples, com apenas dois atributos obrigatórios do mesmo tipo. - Geração de um relatório PDF com dezenas de opções de layout (cabeçalho, rodapé, fontes, cores, colunas, filtros, ordenações, gráficos opcionais).
Para cada item, responda:
- “Faz sentido usar Builder” ou “Não faz sentido usar Builder”.
- Justifique pensando em: número de parâmetros, opcionalidade, legibilidade, reuso do processo de construção.
Exercício 2: Criando uma analogia para Builder
Seção intitulada “Exercício 2: Criando uma analogia para Builder”Crie uma analogia própria para o padrão Builder, diferente da analogia de sanduíche usada em aula/texto.
- Escolha uma situação do mundo real em que algo é montado passo a passo, seguindo um “plano” ou “receita” (por exemplo: montagem de PC, planejamento de viagem, construção de casa, etc.).
- Descreva:
- quem seria o Produto,
- quem (ou o que) faz o papel de Builder,
- e, se fizer sentido, quem seria o Director (quem orquestra os passos).
- Explique por que essa analogia ajuda a entender:
- a separação entre “como montar” e “o que está sendo montado”,
- e a possibilidade de ter variações do mesmo produto seguindo passos parecidos.
Exercício 3: Anti‑pattern: Builder usado de forma desnecessária
Seção intitulada “Exercício 3: Anti‑pattern: Builder usado de forma desnecessária”Analise o código abaixo, inspirado em um sistema acadêmico. A intenção foi “usar Builder para tudo”:
public class CoordenadorBuilder {
private String nome; private String email;
public CoordenadorBuilder nome(String nome) { this.nome = nome; return this; }
public CoordenadorBuilder email(String email) { this.email = email; return this; }
public Coordenador build() { Coordenador c = new Coordenador(); c.setNome(nome); c.setEmail(email); return c; }}
public class Coordenador { private String nome; private String email;
// getters e setters públicos}Considerando que Coordenador tem apenas dois campos obrigatórios (nome e email) e não há muitas variações de construção, responda:
- Por que esse uso de Builder pode ser considerado overengineering (uso desnecessário de complexidade)?
- Quais seriam formas mais simples de criar objetos
Coordenadornesse caso? - Cite pelo menos duas consequências negativas de sair aplicando Builder mesmo onde ele não agrega valor (pense em legibilidade, manutenção, curva de aprendizado para novos membros do time).
Exercício 4: Builder em um projeto open source real
Seção intitulada “Exercício 4: Builder em um projeto open source real”Acesse o seguinte arquivo em um projeto open source:
- Projeto: OkHttp (cliente HTTP popular em Java/Kotlin)
- Arquivo:
Request.Builder
Nesse arquivo, a classe Request possui um Builder interno amplamente usado na prática.
Responda:
- Identifique onde o Builder aparece nesse arquivo e como ele é declarado em relação à classe
Request. - Liste 3 exemplos de métodos do Builder (como
url(...),header(...),post(...)ou similares) e explique, em uma frase cada, qual responsabilidade eles configuram. - Explique qual problema o Builder resolve nesse contexto (pense na quantidade de opções para montar uma requisição HTTP).
- Com base no que você viu, descreva em poucas frases como você aplicaria um padrão de Builder parecido para criar requisições para um serviço REST em um projeto da disciplina.
Exercício 5: Implementando um Builder simples
Seção intitulada “Exercício 5: Implementando um Builder simples”Implemente, em Java, um Builder para criar pizzas personalizadas.
Considere o seguinte cenário:
- Uma pizza tem:
- tamanho (PEQUENA, MEDIA, GRANDE) – obrigatório
- tipo de massa (FINA, GROSSA) – obrigatório
- molho (TOMATE, BRANCO, PESTO) – opcional (padrão: TOMATE)
- lista de coberturas (queijo extra, pepperoni, cebola, milho, etc.) – opcionais (0 ou mais)
- borda recheada (boolean) – opcional (padrão: false)
-
Defina a classe
Pizzacomo imutável (atributosprivate final, sem setters). -
Crie uma classe interna
Builder(ou uma classe separadaPizzaBuilder) com:- Construtor recebendo apenas os campos obrigatórios (tamanho e tipo de massa)
- Métodos encadeáveis para configurar opções:
molho(...)adicionarCobertura(String cobertura)bordaRecheada(boolean bordaRecheada)
- Método
build()que devolve uma instância dePizza.
-
Garanta que:
- Não é possível criar uma
Pizzasem informar tamanho e tipo de massa. - A lista de coberturas é protegida contra modificações externas (por exemplo, usando cópias).
- Não é possível criar uma
-
Escreva um pequeno trecho de código de uso (em um
mainou teste) criando pelo menos duas pizzas diferentes, por exemplo:- Uma pizza grande, massa grossa, borda recheada, com queijo extra e pepperoni.
- Uma pizza média, massa fina, sem borda recheada, com milho e cebola.
-
Comente no código (ou em texto separado) em 3–4 linhas:
- Por que o padrão Builder deixa esse tipo de criação de objeto mais legível?
- Como ele ajuda quando o número de combinações de parâmetros é grande?
Exercício 6: Builder para um cenário real: criação de um objeto de configuração
Seção intitulada “Exercício 6: Builder para um cenário real: criação de um objeto de configuração”Imagine que você está desenvolvendo uma biblioteca Java para integração com um serviço REST externo (por exemplo, um “Serviço de Notificações”). Para inicializar o cliente dessa biblioteca, é necessário passar um objeto de configuração com diversos parâmetros, alguns obrigatórios e vários opcionais.
Contexto
Seção intitulada “Contexto”A configuração desse cliente deve permitir:
-
Obrigatórios:
baseUrl(URL base da API, ex:https://api.meunotify.com)apiKey(chave de autenticação)
-
Opcionais (com valores padrão razoáveis):
connectionTimeoutMs(timeout de conexão em milissegundos, padrão: 5000)readTimeoutMs(timeout de leitura em milissegundos, padrão: 10000)maxRetries(número máximo de tentativas em caso de falha transitória, padrão: 3)useCache(se deve usar cache de respostas GET, padrão:false)logLevel(enum:NONE,BASIC,FULL, padrão:BASIC)
Tarefas
Seção intitulada “Tarefas”-
Defina em Java uma classe imutável
NotificationClientConfigque represente essa configuração.- Todos os atributos devem ser
private final. - Não deve haver setters públicos.
- Os campos obrigatórios não podem ficar sem valor.
- Todos os atributos devem ser
-
Crie um Builder para
NotificationClientConfig:- Pode ser:
- uma classe interna estática
public static class Builder - ou uma classe separada
NotificationClientConfigBuilder.
- uma classe interna estática
- O construtor do Builder deve receber apenas os campos obrigatórios (
baseUrl,apiKey). - Os parâmetros opcionais devem ter valores padrão definidos no Builder.
- Implemente métodos encadeáveis para configurar os campos opcionais, por exemplo:
public Builder connectionTimeoutMs(int timeoutMs) { ... }public Builder readTimeoutMs(int timeoutMs) { ... }public Builder maxRetries(int maxRetries) { ... }public Builder useCache(boolean useCache) { ... }public Builder logLevel(LogLevel logLevel) { ... }
- Implemente o método
build()que retorna uma instância pronta deNotificationClientConfig.
- Pode ser:
-
Garanta que:
- Não seja possível criar um
NotificationClientConfigsem informarbaseUrleapiKey. - Os valores padrão são aplicados corretamente quando o usuário do Builder não configura explicitamente algum campo opcional.
- Não seja possível criar um
-
Escreva um pequeno trecho de código (em um
mainou teste) mostrando dois exemplos de uso do Builder:- Um cliente bem simples, usando apenas os parâmetros obrigatórios.
- Um cliente “mais avançado”, configurando explícita e fluentemente vários parâmetros opcionais (por exemplo, alterando timeouts, habilitando cache, mudando o nível de log).