DEV Community

TDA (Tell Don't Ask)

Introdução

A visão original de Kay para OOP não era "objetos com dados públicos que outros manipulam", era objetos que trocam mensagens e decidem sozinhos o que fazer com elas. Tell, Don't Ask é basicamente um resgate dessa ideia original, porque com o tempo muita gente passou a usar OOP como "structs com getters e setters", perdendo o encapsulamento de verdade.

Ideia Central

Exemplo do Cliente e Carteira

Ask - Eu PERGUNTO o saldo, e EU decido o que fazer com ele:

if (cliente.carteira.saldo >= 50) {
    cliente.carteira.saldo -= 50;
} else {
    console.log("saldo insuficiente");
}

Tell - Eu DIGO pro cliente pagar, e ELE decide o que fazer:

// "Tell" - eu DIGO pro cliente pagar, e ELE decide o que fazer
cliente.pagar(50);

class Cliente {
    carteira: Carteira;

    pagar(valor: number) {
        if (this.carteira.saldo < valor) {
            throw new Error("saldo insuficiente");
        }
        this.carteira.saldo -= valor;
    }
}

Repare a diferença de responsabilidade: No "Ask", quem chama o código precisa saber a regra ("se o saldo for menor, não pode pagar") e tomar a decisão sozinho. No "Tell", o próprio objeto conhece sua regra e decide por dentro. Quem chama só diz o que quer que aconteça.

Por que "perguntar" é perigoso

Pensa no "Ask" espalhado pelo sistema: toda tela, todo botão, todo endpoint que cobra do cliente vai ter que copiar essa mesma verificação de saldo:

if (cliente.carteira.saldo >= valorDoCarrinho) { ... }
if (cliente.carteira.saldo >= valorDaAssinatura) { ... }
if (cliente.carteira.saldo >= valorDoBoleto) { ... }

Se um dia a regra mudar (por exemplo, "clientes VIP podem ficar com saldo negativo até -R$100"), você precisa caçar todos esses lugares e mudar um por um. É praticamente garantido que algum lugar vai ser esquecido - e aí seu sistema tem um bug de regra de negócio inconsistente.

Com "Tell", a regra mora em um lugar só (Cliente.pagar). Mudar uma vez, resolve todo o sistema.

Como isso conecta com Law of Demeter

Os dois princípios andam juntos, mas resolvem problemas ligeiramente diferentes:

  • Law of Demeter: não atravesse a estrutura interna de outros objetos (a.b.c).
  • Tell, Don't Ask: não extraia dados de um objeto pra decidir algo fora dele - mande o objeto decidir.

Na prática, quando você aplica bem "Tell, Don't Ask", quase sempre acaba respeitando Law of Demeter de graça - porque você para de precisar alcançar o carteira.saldo de fora.

O sinal de alerta pra reconhecer

Pergunte-se, olhando pro código: "Estou pegando um dado de dentro de um objeto só pra, logo em seguida, tomar uma decisão (if) ou fazer uma conta com esse dado?"

Se sim, é sinal de "Ask". Pergunte então: "quem deveria saber essa regra: eu (quem está chamando) ou o objeto dono do dado?" Quase sempre é o objeto dono.

// Sinal de alerta: pegou o dado (idade) e decidiu por fora
if (usuario.idade >= 18) {
    liberarCompra();
}

// Melhor: quem sabe a regra de maioridade é o próprio usuário
if (usuario.podeComprar()) {
    liberarCompra();
}

Quando NÃO aplicar ao pé da letra

  • Código que só exibe dados (uma tela mostrando o saldo do cliente) precisa "perguntar" - não tem decisão de negócio envolvida, só exibição. Tell, Don't Ask é sobre decisões e comportamento, não sobre leitura pura de dados para exibir.
  • Structs/DTOs (objetos que só carregam dados entre camadas, tipo resposta de API) não precisam desse princípio - eles não têm comportamento por definição.

Comments

No comments yet. Start the discussion.