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!

É isso. Até a próxima!

Anúncios

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

    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

  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.

    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!

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 )

Imagem do Twitter

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

Foto do Facebook

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

Foto do Google+

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

Conectando a %s