12  Design orientado a objetos

Encapsulamento, coesão e composição no chatbot

12.1 Objetivos de aprendizagem

Ao concluir este capítulo, você deverá ser capaz de:

  • distribuir responsabilidades entre classes coesas;
  • proteger invariantes com encapsulamento;
  • escolher modificadores de acesso;
  • usar composição para colaborar sem herança;
  • identificar acoplamento desnecessário.

12.2 Da classe grande às responsabilidades

A aplicação de console usa um processador de comandos, que consulta um catálogo e registra o histórico.

Responsabilidades separadas colaboram por composição.

A versão anterior concentra entrada, regras e histórico. Uma primeira decomposição pode criar:

  • Historico: registra e consulta interações;
  • ProcessadorComandos: escolhe respostas;
  • AplicacaoConsole: coordena terminal e encerramento.

Uma classe é coesa quando seus dados e métodos trabalham para o mesmo objetivo. Acoplamento é a dependência entre partes; o objetivo não é eliminá-lo, mas depender de contratos pequenos e estáveis.

12.3 Encapsulamento e acesso

Modificador Acesso principal
private somente a própria classe
sem modificador classes do mesmo pacote
protected mesmo pacote e subclasses
public qualquer classe com acesso ao tipo

Comece com a menor visibilidade necessária. Tornar um atributo private não basta: a classe também deve oferecer operações que preservem seu estado válido.

12.4 Composição

import java.util.ArrayList;
import java.util.List;

final class Historico {
    private final List<String> linhas = new ArrayList<>();

    void registrarInteracao(String entrada, String resposta) {
        if (entrada == null || entrada.isBlank()) {
            throw new IllegalArgumentException("Entrada obrigatória.");
        }
        linhas.add("USUÁRIO: " + entrada.strip());
        linhas.add("BOT: " + resposta);
    }

    List<String> consultar() {
        return List.copyOf(linhas);
    }

    int totalInteracoes() {
        return linhas.size() / 2;
    }
}

final class ProcessadorComandos {
    String responder(String comando) {
        return switch (comando) {
            case "oi" -> "Olá!";
            case "ajuda" -> "Comandos: oi, ajuda e dica.";
            case "dica" -> "Prefira composição para relações do tipo tem-um.";
            default -> "Não entendi.";
        };
    }
}

public class Capitulo11Design {
    public static void main(String[] args) {
        Historico historico = new Historico();
        ProcessadorComandos processador = new ProcessadorComandos();

        String entrada = "dica";
        String resposta = processador.responder(entrada);
        historico.registrarInteracao(entrada, resposta);

        System.out.println(resposta);
        System.out.println(historico.consultar());
    }
}

Historico não decide respostas. ProcessadorComandos não escreve no console nem conhece a estrutura do histórico. A aplicação compõe os objetos.

12.5 static e final

Use static para comportamento ou estado que pertence à classe, não a uma instância específica. Evite transformá-lo em atalho para estado global mutável.

Use final para comunicar que:

  • uma referência não será reatribuída;
  • um método não poderá ser sobrescrito;
  • uma classe não foi projetada para herança.

12.6 Revisão de design

Pergunte:

  1. cada classe muda por um motivo principal?
  2. uma regra aparece em apenas um lugar?
  3. as coleções internas estão protegidas?
  4. seria possível trocar a interface de console sem reescrever o domínio?
  5. os nomes usam a linguagem do problema?

O exemplo completo está em examples/src/Capitulo11Design.java.

12.7 Prática verificável

Extraia da versão 10 uma classe Historico. A aplicação deve continuar produzindo as mesmas respostas.

Casos de teste:

  • histórico vazio devolve uma lista vazia;
  • uma interação gera exatamente duas linhas;
  • alterar a lista devolvida não altera o estado interno;
  • entrada vazia é rejeitada.
DicaRegra de modelagem

Use composição para relações “tem um”: o chatbot tem um histórico. Herança será reservada a relações legítimas de especialização.