Se há apenas duas possibilidades, uso if/else
apenas como ternário:
return foo === null
? fizz
: buzz
Se multiples possibilidades E é preciso fazer um processo de eliminação (ex: eliminar os casos de erro), uso if
s com early return.
Inclusive tratar os erros primeiro é algo recomendado em Object Calisthenics (pesquisar por "Object Calisthenics ~programming").
A ideia é tratar os erros o quanto antes para que a logica de sucesso esteja disposta da forma mais clara o possível. Não se trata de performance, mas sim, de legibilidade.
const res = await fetch(/* */)
if (res.status === 401) {
throw new AuthenticationRequiredError(/* ... */)
}
if (res.status === 403) {
throw new NotAuthorizedError(/* ... */)
}
if (res.status === 422) {
throw new ValidationError(/* ... */)
}
if (res.status !== 200) {
throw new SomethingWrongIsntRightError(/* ... */)
}
return { /* ... */ }
Raramente uso mas é útil quando se tem um campo discriminatório à partir do qual será feita uma "bifurcação" (branching) de código. É mais legível quando se há uma quantidade limitada e pequena de opções (tipo um Enum do TS). Considero mais adequado quando o statement para cada opção é curto (one-liner). Quando o statemente é maior do que isso ou quando há muitas opções, considere usar Strategies.
function validarDocumento(documento) {
switch (documento.tipo) {
case 'cpf':
return validarCpf(documento.numero)
case 'passaporte':
return validarPassaporte(documento.numero)
case 'cnpj':
return validarCnpj(documento.numero)
default:
throw new DocumentoNaoSuportado(/* ... */)
}
}
Quando um switch está se tornando insustentável por ter muitas opções (baixa legibilidade e/ou baixa clareza), dá pra fazer essa gamb do bem:
const VALIDADORES = {
cpf: validarCpf,
passaporte: validarPassaport,
cnpj: validarCnpj
}
function validarDocumento(documento) {
const validador = VALIDADORES[documento.tipo]
if (validador === undefined) {
throw new DocumentoNaoSuportado(/* ... */)
}
return validador(documento.numero)
}
No entanto, quando a quantidade de possibilidades é grande e/ou novas possibilidades continuam sendo adicionadas e/ou a condição é mais complexa do que apenas um campo discriminatório, provavelmente já é hora de partir para um padrão de design de Estratégias. Meu exemplo vai ser bem basicão, faz dá pra fazer mais "fancy" também (extrair cada estratégia em seu arquivo e se pá usar classes se esse for o tipo de droga que vc usa, etc).
// const cpf = require('./strategies/cpf.js')
// const passaporte = require('./strategies/passaporte.js')
// const cnpj = require('./strategies/cnpj.js')
const cpf = {
matches: ({ tipo }) => tipo === 'cpf',
validade: ({ número }) => /* logica de validar aqui, ou chama a fn de validar cpf se vc precisar reutilizá-la */
}
const passaporte = {
matches: ({ tipo }) => tipo === 'passaporte',
validate: ({ numero }) => /* ... */
}
const cnpj = {
matches: ({ tipo }) => tipo === 'cnpj',
validate: ({ numero }) => /* ... */
}
// ordenado por prioridade e/ou documento mais provável pois
// a seleção da estratégia correta tem tempo-complexidade `O(n)`
// no pior caso.
const STRATEGIES = [
cpf,
passaporte,
cnpj,
// ...
]
function validarDocumento(documento) {
const validador = VALIDADORES.find((validador) => validador.matches(documento))
if (validador === undefined) {
throw new DocumentoNaoSuportado(/* ... */)
}
return validador.validate(documento)
}
Segue seu coração, prefira legibilidade e clareza sempre que possível, e, na dúvida, consulte seu time para escolher qual opção fica melhor.