Tell, don’t ask

“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!

6 comentários em “Tell, don’t ask

Adicione o seu

    1. Olá, Jonatan!

      É uma opção, porém estou assumindo que ali é o único lugar que um item será usado e como ele é parte do pedido, nada mais justo que o pedido saiba como criá-lo. O “mundo externo” nem precisa saber da existência de um item.
      ——————————————————-

      Qto à qtd de parâmetros, são apenas 2 e bem coesos com o propósito do método.. Se a qtd de parâmetros fosse maior, aí poderia ser o caso de encapsulá-los em 1 (ou mais) objetos ou ainda poderia haver um problema maior: o método estar fazendo coisa demais.

      []s

      Curtir

  1. Post excelente Robson!!!
    Até hoje, nos meus 8 anos de experiência eu vi apenas 1 software realmente OO, e eu já trabalhei em várias empresas com C#.É possível ver que
    as pessoas ainda não entenderam OO, por isso, temos pessoas programando procedural em linguagens que fornecem todos os meios para programar bem
    orientado a objeto.
    Só como exemplo do que eu já vi muitas vezes, o programador cria uma classe cliente, e dentro dela “SOCA” qualquer método que faça referência ao
    cliente, deixando a classe com mais de 1000 linhas, sem seguir o SPR, DRY, estado, comportamente e qualquer outro conceito que faça com que aquele
    código seja OO.

    Curtir

    1. Obrigado, Roger

      Escrever código “direito” é bem difícil e só há poucos anos que as melhores práticas se tornaram mais difundidas. Entretanto, o mal já estava feito (legados e mais legados mal-escritos que ainda formam a maior parte do código mantido hoje em dia).

      []s!

      Curtir

  2. Ótima postagem, assunto de grande utilidade para escrevermos código de melhor qualidade. Pena que estas boas práticas não foram difundidas antes e vamos sofrer um bom tempo com código legado OO escrito de forma procedural.

    Curtir

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Blog no WordPress.com.

Acima ↑

%d blogueiros gostam disto: