Deixar um código coeso e com baixo acoplamento é difícil.
Mas um dos princípios que pode nos ajudar a tornar nosso código melhor é o “Tell, Don’t Ask”.
Vamos aprender um pouco sobre este princípio e ver como aplicar em um código de exemplo.
Definição#
“Tell, Don’t Ask” pode ser entendido como
“Diga o que devo fazer, e não me peça por informações

A ideia é que lógicas e os dados não devem ficar separadas, mas juntas.
No mundo da programação orientada à objetos, isso significa que as classe são responsáveis pelas lógicas e os seu dados.
Mas como aplicar?
Vamos ver um exemplo com um projeto simples
Exemplo#
Imagine a seguinte situação:
package br.com.youready.article.d_2024_11_18.image2;
public enum ManchesterProtocolClassification {
IMMEDIATE,
VERY_URGENT,
URGENT,
STANDARD,
NON_URGENT;
}
package br.com.youready.article.d_2024_11_18.image2;
import java.time.Instant;
import lombok.Getter;
@Getter
public class Patient {
private final String name;
private final ManchesterProtocolClassification classification;
private final Instant arrivalTime;
public Patient(String name, ManchesterProtocolClassification classification) {
this.name = name;
this.classification = classification;
this.arrivalTime = Instant.now();
}
public Instant getArrivalTime() {
return arrivalTime;
}
public ManchesterProtocolClassification getClassification() {
return classification;
}
}
package br.com.youready.article.d_2024_11_18.image2;
import java.time.Duration;
import java.time.Instant;
public class TriageService {
public boolean isWaitingTimeExceeded(Patient patient) {
Duration waitingTime = Duration.between(patient.getArrivalTime(), Instant.now());
Duration maxWaitTime =
switch (patient.getClassification()) {
case IMMEDIATE -> Duration.ofMinutes(0);
case VERY_URGENT -> Duration.ofMinutes(10);
case URGENT -> Duration.ofMinutes(60);
case STANDARD -> Duration.ofHours(2);
case NON_URGENT -> Duration.ofHours(4);
};
return waitingTime.compareTo(maxWaitTime) > 0;
}
}
TriageService é um serviço responsável verificar se o tempo de espera do paciente ultrapassou de acordo com a classificação do paciente.
Patient é a classe que possui o nome e o instante de chegada do paciente.
ManchesterProtocolClassification é um enum com as possíveis classificações.
É um código com uma estrutura típica que vemos em muitos projetos. Mas pode ser melhorada aplicando o Tell, Don’t Ask.
Vamos focar no método isWaitingTimeExceeded.
Onde está o problema#
Veja que o método isWaitingTimeExceeded depende de dois dados do paciente:
- patient.getArrivalTime()
- patient.getClassification()
Ou seja, a lógica e os dados estão separados:
- Lógica: TriageService#isWaitingTimeExceeded
- Dados: Objeto patient
Se formos aplicar o “Tell, Don’t Ask” nesta situação, seria:
Não peça informações para o objeto patient mas diga o que ele deve fazer.
Ou seja, devemos perguntar para o objeto patient se o tempo de espera excedeu:
package br.com.youready.article.d_2024_11_18.image3;
public class TriageService {
public boolean isWaitingTimeExceeded(Patient patient) {
// Aplicamos o tell, dont ask!
return patient.isWaitingTimeExceeded();
}
}
package br.com.youready.article.d_2024_11_18.image3;
import java.time.Duration;
import java.time.Instant;
import lombok.Getter;
@Getter
public class Patient {
private final String name;
private final ManchesterProtocolClassification classification;
private final Instant arrivalTime;
public Patient(String name, ManchesterProtocolClassification classification) {
this.name = name;
this.classification = classification;
this.arrivalTime = Instant.now();
}
public Instant getArrivalTime() {
return arrivalTime;
}
public ManchesterProtocolClassification getClassification() {
return classification;
}
// Movemos a lógica para a classe
public boolean isWaitingTimeExceeded() {
Duration waitingTime = Duration.between(this.getArrivalTime(), Instant.now());
Duration maxWaitTime =
switch (this.getClassification()) {
case IMMEDIATE -> Duration.ofMinutes(0);
case VERY_URGENT -> Duration.ofMinutes(10);
case URGENT -> Duration.ofMinutes(60);
case STANDARD -> Duration.ofHours(2);
case NON_URGENT -> Duration.ofHours(4);
};
return waitingTime.compareTo(maxWaitTime) > 0;
}
}
Esta versão do código é melhor em alguns pontos:
- Se eu escrever um teste unitário para verificar a regra de negócio do tempo de espera, este teste depende somente do objeto Patient.
- Reduzimos o acoplamento entre a classe TriageService e o enum ManchesterProtocolClassification. Isso significa que posso realizar alterações no ManchesterProtocolClassification sem a preocupação de modificar o TriageService.
Show!
Diminuímos o custo para escrever os testes e também diminuímos o custo da alteração!
Vamos continuar aplicando o “Tell, Don’t Ask”!
Eliminando o switch#
Agora vamos olhar para o método Patient#isWaitingTimeExceeded.
Conseguem eliminar o switch aplicando o “Tell, Don’t Ask”?
Vamos pensar.
A lógica, para funcionar, precisa do tempo de chegada e o valor da classificação. Cada informação está em:
- tempo de chegada - objeto Patient
- valor da classificação - valor de cada enum de ManchesterProtocolClassification
Ou seja, já que a propriedade classification do objeto Patient possui o valor do enum, podemos dizer para o classification o que ele deve fazer!
Ficaria assim:
package br.com.youready.article.d_2024_11_18.image4;
import java.time.Duration;
enum ManchesterProtocolClassification {
IMMEDIATE(Duration.ofMinutes(0)),
VERY_URGENT(Duration.ofMinutes(10)),
URGENT(Duration.ofMinutes(60)),
STANDARD(Duration.ofHours(2)),
NON_URGENT(Duration.ofHours(4));
private final Duration maxWaitTime;
ManchesterProtocolClassification(Duration maxWaitTime) {
this.maxWaitTime = maxWaitTime;
}
public boolean isWaitingTimeExceed(Duration waitingTime) {
return waitingTime.compareTo(maxWaitTime) > 0;
}
}
package br.com.youready.article.d_2024_11_18.image4;
import java.time.Duration;
import java.time.Instant;
import lombok.Getter;
@Getter
public class Patient {
private final String name;
private final ManchesterProtocolClassification classification;
private final Instant arrivalTime;
public Patient(String name, ManchesterProtocolClassification classification) {
this.name = name;
this.classification = classification;
this.arrivalTime = Instant.now();
}
public Instant getArrivalTime() {
return arrivalTime;
}
public ManchesterProtocolClassification getClassification() {
return classification;
}
public boolean isWaitingTimeExceeded() {
Duration waitingTime = Duration.between(this.getArrivalTime(), Instant.now());
return this.getClassification().isWaitingTimeExceed(waitingTime);
}
}
package br.com.youready.article.d_2024_11_18.image4;
public class TriageService {
public boolean isWaitingTimeExceeded(Patient patient) {
return patient.isWaitingTimeExceeded();
}
}
Agora sim o código está mais coeso, pois cada lógica está junto com os dados necessários para a sua execução.
E percebam uma coisa interessante: O switch sumiu!
Não precisamos de comparação como if, else e switch para executar a lógica!
Então aqui fica uma dica:
Se tiver um if, else ou switch no seu código e você estiver utilizando dados de um objeto para realizar sua lógica, pode ser um ponto com a possibilidade de aplicar o “Tell, Don’t Ask”.
Conclusão#
Estamos acostumados ou familiarizados com códigos que pedem informações para outros objetos e módulos.
O princípio “Tell, Don’t Ask” serve para nos lembrar que existe uma outra maneira de escrever seu código.
Abstração e encapsulamento são a chave mas se estiver com dúvida de quando aplicar, a pergunta que você sempre pode fazer é:
Estou pedindo uma informação ou estou dizendo o que deve ser feito?
Se você tiver dúvidas, sugestões ou até correções, sinta-se à vontade para comentar ou falar diretamente comigo!
🥒🥒 Até o próximo artigo! 🥒🥒


