Sei que isso pode ser muito específico para casos de uso, mas me pergunto isso com muita frequência. Existe uma sintaxe geralmente preferida.
Não estou perguntando qual é a melhor abordagem quando em uma função, estou perguntando se devo sair mais cedo ou simplesmente não devo chamar a função.
Envoltório se chamada de função ao redor
if (shouldThisRun) {
runFunction();
}
Ter se ( guarda ) em função
runFunction() {
if (!shouldThisRun) return;
}
A última opção obviamente tem o potencial de reduzir a duplicação de código se essa função for chamada várias vezes, mas às vezes parece errado adicioná-la aqui, pois você pode estar perdendo a responsabilidade única da função.
Heres um exemplo
Se eu tiver uma função updateStatus () que simplesmente atualize o status de algo. Quero apenas o status atualizado se o status tiver sido alterado. Conheço os lugares no meu código em que o status tem o potencial de mudar e conheço outros lugares nos quais ele desafiadoramente mudou.
Não tenho certeza se sou apenas eu, mas parece um pouco sujo verificar essa função interna, pois quero manter essa função o mais pura possível - se eu chamá-lo, espero que o status seja atualizado. Mas não sei dizer se é melhor encerrar a ligação com um cheque nos poucos lugares em que sei que tem o potencial de não ter mudado.
fonte
Respostas:
Encerrando um if em torno de uma chamada de função:
decide se a função deve ser chamada e faz parte do processo de tomada de decisão do seu programa.
Cláusula de guarda na função (retorno antecipado):
serve para proteger contra chamadas com parâmetros inválidos
Uma cláusula de guarda usada dessa maneira mantém a função "pura" (seu termo). Existe apenas para garantir que a função não seja interrompida por dados de entrada incorretos.
A lógica sobre a possibilidade de chamar a função está em um nível mais alto de abstração, mesmo que apenas. Deve existir fora da própria função. Como o DocBrown diz, você pode ter uma função intermediária que executa essa verificação, para simplificar o código.
Essa é uma boa pergunta, e se enquadra no conjunto de perguntas que levam ao reconhecimento de níveis de abstração. Cada função deve operar em um único nível de abstração, e ter a lógica do programa e a lógica da função na função parece errada para você - isso ocorre porque elas estão em diferentes níveis de abstração. A função em si é um nível mais baixo.
Ao mantê-los separados, você garante que seu código será mais fácil de escrever, ler e manter.
fonte
Você pode ter os dois - uma função que não verifica os parâmetros e outra que faz isso, assim (talvez retornando algumas informações sobre se a chamada foi feita):
Dessa forma, você pode evitar lógica duplicada fornecendo uma função reutilizável
tryRunFunction
e ainda ter sua função original (talvez pura) que não faz a verificação interna.Observe que algumas vezes você precisará de uma função como
tryRunFunction
uma verificação integrada exclusivamente, para poder integrar a verificaçãorunFunction
. Ou você não precisa reutilizar a verificação em nenhum lugar do seu programa novamente; nesse caso, você pode deixá-la permanecer na função de chamada.No entanto, tente tornar transparente ao chamador o que acontece, dando nomes adequados às suas funções. Portanto, os chamadores não precisam adivinhar ou analisar a implementação se tiverem que fazer as verificações sozinhos ou se a função chamada já o fizer. Um prefixo simples como
try
geralmente pode ser suficiente para isso.fonte
runFunction
. Uma função comoupdateStatus()
pode ser acompanhada por outra função comoupdateIfStatusHasChanged()
. Mas isso depende 100% do caso, não há uma solução "tamanho único" para isso, então sim, eu concordo, o idioma "try" nem sempre é uma boa escolha.Quanto a quem decide se candidatar, a resposta é, do GRASP , quem é o "especialista em informações" que sabe.
Depois de decidir, considere renomear a função para maior clareza.
Algo assim, se a função decidir:
Ou, se o chamador decidir:
fonte
Eu gostaria de expandir a resposta de @ Baldrickk.
Não há resposta geral para sua pergunta. Depende do significado (contrato) da função a ser chamada e da natureza da condição.
Então, vamos discutir isso no contexto da sua chamada de exemplo
updateStatus()
. Provavelmente, o contrato é atualizar algum status, porque algo com influência no status aconteceu. Eu esperaria que as chamadas para esse método fossem permitidas, mesmo que não houvesse mudança de status real, e seriam necessárias se houver uma mudança real.Portanto, um site de chamada pode pular uma chamada
updateStatus()
se souber que (dentro do horizonte de seu domínio) nada relevante foi alterado. Essas são as situações em que a chamada deve ser cercada por umaif
construção apropriada .Dentro da
updateStatus()
função, pode haver situações em que essa função detecta (a partir de dados dentro de seu horizonte de domínio) que não há nada a fazer, e é para isso que ela deve retornar mais cedo.Então, as perguntas são:
Com uma
updateStatus()
função, eu esperaria ver os dois, chamando sites que nada sabem que mudou, ignorando a chamada e a implementação verificando situações "alteradas por nada" mais cedo, mesmo que dessa maneira a mesma condição seja verificada duas vezes, ambos dentro e fora.fonte
Há muitas boas explicações. Mas eu quero parecer incomum: Suponha que você use dessa maneira:
E você precisa chamar outra função no
runFunction
método como este:O que você vai fazer? Você copia todas as validações de cima para baixo?
Acho que não. Portanto, costumo fazer a mesma abordagem: valide entradas e verifique condições no
public
método. Os métodos públicos devem fazer suas próprias validações e verificar as condições necessárias, mesmo as chamadas. Mas deixe que os métodos privados façam seus próprios negócios . Alguma outra função pode chamarrunFunction
sem fazer nenhuma validação ou verificar qualquer condição.fonte