“Tell, don’t ask” é uma das práticas mais importantes da orientação a objetos, pois tem por maior objetivo reforçar a ideia de encapsulamento, conceito fundamental desse paradigma.

O nome vem do fato de que devemos dizer (tell) ao objeto o que fazer ao invés de perguntarmos (ask) ao objeto sobre seu estado e tomarmos alguma decisão.

Vamos a um exemplo onde “Tell, don’t ask” não é aplicado:

// ---- código-consumidor do objeto "carro" ----

carro.AcelerarPara(150);
// violando o "Tell, don't ask"
if (carro.VelocidadeAtual > carro.VelocidadeMaxima)
    throw new Exception("Velocidade superior à velocidade máxima!");

Observem que, apesar de “dizer” para o carro acelerar, o código ainda pede ao carro sua velocidade atual e a velocidade máxima para fazer uma validação.

Por que não deixar que o carro “se vire” com suas próprias regras de aceleração?

// na classe Carro fica assim:
public class Carro
{
    private int _velocidadeAtual;
    private int _velocidadeMaxima;

    // outros membros da classe

    public void AcelerarPara(int velocidade)
    {
        if (velocidade > _velocidadeMaxima)
            throw new Exception("Velocidade superior à velocidade máxima!");
        _velocidadeAtual = velocidade;
    }
}

// ---- código-consumidor do objeto carro ----
carro.AcelerarPara(150);

Agora sim! Veremos alguns problemas da primeira abordagem mais abaixo. Antes vamos a outro exemplo. Neste, o objeto possui uma coleção, que é manipulada fora dele.

// ---- código-consumidor do objeto "pedido" ----

// criando o item novo
var novoItem = new ItemDoPedido(produto, quantidade);
// violando o "Tell, don't ask"
pedido.Itens.Add(novoItem);

Percebam que estamos pedindo a lista de itens ao pedido e, então, adicionando um item novo a essa lista. Isso quebra claramente o encapsulamento da classe Pedido.

Por que não dizer ao pedido que adicione um item para nós? Vejamos uma forma de se fazer isso:

// na classe Pedido fica assim:
public class Pedido
{
    private IList<ItemDoPedido> _itens = new List<ItemDoPedido>();

    // outros membros da classe

    public void Adicionar(Produto produto, int quantidade)
    {
        var novoItem = new ItemDoPedido(produto, quantidade);
        _itens.Add(novoItem);
    }
}

// ---- código-consumidor do objeto "pedido" ----
// agora seguindo o "Tell, don't ask"
pedido.Adicionar(produto, quantidade);

Muito melhor, não? Agora é responsabilidade do pedido adicionar um item novo. O encapsulamento dos itens foi preservado. Qualquer alteração na regra de adicionar um item – como uma validação – é feita somente em um único lugar do software.

(Embora não seja foco do exemplo nem do artigo em si, notem que eu também passei a responsabilidade de criar o item para o próprio pedido, no método Adicionar, imaginando que isso fosse necessário somente ali.)

ISSO NÃO É OO!

Códigos que não consideram o “Tell, don’t ask” são códigos PROCEDURAIS. Um objeto de verdade possui dados e comportamento para manipular esses dados (princípio básico de orientação a objetos).

Código procedural não expressa claramente que ação está sendo executada. Ao invés de algo objetivo como “EncerrarConta”, podemos ter uma série de condições e atribuições em cima de um ou mais dados do objeto.

Além disso, damos margem para que uma mesma lógica se repita em mais de um lugar do software. E sabemos que duplicação de código é do mal!

Esses problemas são potencializados a medida que a base de código cresce e se torna mais complexa.

Centralizando dados e comportamento em um mesmo lugar, evitamos os problemas acima e facilitamos a manutenção do código.

RESUMINDO TUDO…

Diga ao objeto o que você quer que ele faça!

É isso. Até a próxima!