Devo lançar uma exceção ou retornar um erro?

Retornar um erro ou lançar uma exceção têm usos e vantagens distintas. Assim como em quase qualquer técnica dentro da programação vai depender do contexto que você vai aplicar.

Retornar um erro é útil quando o fluxo normal do programa pode lidar com a falha de maneira graciosa, sem interromper o funcionamento geral do software. Ao retornar um erro ao invés de lançar uma exceção você torna o código mais previsível e fácil de depurar, especialmente em sistemas menores e menos complexos.

Já lançar uma exceção é mais adequado para erros críticos que não podem ser ignorados, falhas imprevisíveis, ou quando a falha de execução precisa ser tratada por um nível superior do código.

Para lançar uma exceção basta usar o mecânismo de lançamento de exceções da própria linguagem de programação, porém, muita gente desconhece algumas técnicas para retornar um erro. Nesse artigo vamos abordar três técnicas simples e seus casos de uso. São conhecidas como Result, Either e Option/Maybe.

1. Result

Se você usa TypeScript em seu projeto, uma abordagem comum para retornar um erro é usar o tipo Result. A ideia é definir um tipo que pode representar tanto um valor de sucesso quanto um valor de erro. Exemplo de código:

type Result<T> = { success: true, value: T } | { success: false, error: string };

function dividir(a: number, b: number): Result<number> {
  if (b === 0) {
    return { success: false, error: "Divisão por zero" };
  }
  return { success: true, value: a / b };
}

const resultado = dividir(10, 0);

if (resultado.success) {
  console.log("Resultado:", resultado.value);
} else {
  console.log("Erro:", resultado.error);
}

Neste exemplo, a função dividir retorna um objeto que indica se a operação foi bem-sucedida ou se houve um erro. Assim, você pode lidar com o erro de maneira controlada no seu código.

2. Either

A técnica Either é bastante usada em linguagens funcionais, como Haskell e Scala, mas pode ser aplicada em TypeScript e outras liguagens mais flexíveis. Ela é similar ao conceito de Result. Um Either pode conter dois valores diferentes: Left, geralmente representando um erro, e Right, representando um sucesso. Exemplo de código:

type Either<L, R> = { tag: 'left', left: L } | { tag: 'right', right: R };

function dividir(a: number, b: number): Either<string, number> {
  if (b === 0) {
    return { tag: 'left', left: "Divisão por zero" };
  }
  return { tag: 'right', right: a / b };
}

const resultado = dividir(10, 0);

if (resultado.tag === 'right') {
  console.log("Resultado:", resultado.right);
} else {
  console.log("Erro:", resultado.left);
}

Aqui, usamos tag para diferenciar entre Left e Right. Essa técnica é poderosa porque permite tratar os casos de erro e sucesso de forma clara e explícita. Há outras implementações para o Either, mas vamos ficar com a implementação acima que é bem simples de entender.

3. Option

Outra bastante comum é o Option ou Maybe, que é usado para representar a presença ou ausência de um valor. Diferente do Either, que lida com duas alternativas possíveis (sucesso ou erro), o Option lida com valores opcionais. Exemplo de código:

type Option<T> = { tag: 'some', value: T } | { tag: 'none' };

function encontrarValor(chave: string, mapa: Map<string, number>): Option<number> {
  if (mapa.has(chave)) {
    return { tag: 'some', value: mapa.get(chave)! };
  }
  return { tag: 'none' };
}

const mapa = new Map<string, number>([['a', 1], ['b', 2]]);
const resultado = encontrarValor('c', mapa);

if (resultado.tag === 'some') {
  console.log("Valor encontrado:", resultado.value);
} else {
  console.log("Valor não encontrado");
}

Essa técnica é útil para evitar null ou undefined no seu código, tornando o fluxo de dados mais seguro e previsível.

Concluindo:

A escolha entre Result, Either, Option ou Maybe depende muito do contexto em que você está programando. Aqui estão algumas considerações que podem ajudar a decidir:

  • Result: Ideal para funções que podem retornar sucesso ou um erro específico. Útil em operações que têm uma alta probabilidade de falhar, como chamadas de rede ou operações de E/S.
  • Either: Semelhante ao Result, mas mais genérico, podendo representar qualquer tipo de sucesso ou falha. Útil quando você tem duas alternativas claras.
  • Option/Maybe: Melhor para representar a presença ou ausência de um valor. Excelente para funções que podem retornar um valor ou nada, como procurar um elemento em um mapa ou acessar dados opcionais.

Cada uma dessas técnicas tem seu lugar e pode ser mais adequada em diferentes cenários. Para escolher uma "melhor", vai depender totalmente do tipo de retorno que você espera lidar e da clareza que você deseja no seu código.

Comentários

Postagens mais visitadas deste blog

Um caso de uso não deve depender de outro caso de uso

O que são Aggregates?

Repositórios não devem ser disponibilizado para os clientes