Costumo encontrar-me retornando um booleano de um método, usado em vários locais, para conter toda a lógica em torno desse método em um único local. Todo o método de chamada (interno) precisa saber é se a operação foi bem-sucedida ou não.
Estou usando Python, mas a pergunta não é necessariamente específica para essa linguagem. Existem apenas duas opções em que posso pensar
- Crie uma exceção, embora as circunstâncias não sejam excepcionais, e lembre-se de capturar essa exceção em todos os lugares em que a função for chamada
- Retorne um booleano como estou fazendo.
Este é realmente um exemplo simples que demonstra do que estou falando.
import os
class DoSomething(object):
def remove_file(self, filename):
try:
os.remove(filename)
except OSError:
return False
return True
def process_file(self, filename):
do_something()
if remove_file(filename):
do_something_else()
Embora seja funcional, eu realmente não gosto dessa maneira de fazer algo, "cheira" e às vezes pode resultar em muitos ifs aninhados. Mas não consigo pensar em uma maneira mais simples.
Eu poderia recorrer a uma filosofia mais LBYL e usá-lo os.path.exists(filename)
antes de tentar excluir, mas não há garantias de que o arquivo não será bloqueado nesse meio tempo (é improvável, mas possível) e ainda preciso determinar se a exclusão foi bem-sucedida ou não.
Esse é um design "aceitável" e, se não, qual seria a melhor maneira de projetar isso?
Sua intuição está correta, existe uma maneira melhor de fazer isso: mônadas .
O que são mônadas?
Mônadas são (para parafrasear a Wikipedia) uma maneira de encadear operações enquanto ocultam o mecanismo de encadeamento; no seu caso, o mecanismo de encadeamento é o
if
s aninhado . Esconda isso e seu código terá um cheiro muito melhor.Existem algumas mônadas que farão exatamente isso ("Talvez" e "Qualquer uma") e, para sua sorte, elas fazem parte de uma biblioteca de mônadas python realmente agradável!
O que eles podem fazer pelo seu código
Aqui está um exemplo usando a mônada "Qualquer um" ("Disponível" na biblioteca vinculada a), onde uma função pode retornar um Sucesso ou Falha, dependendo do que ocorreu:
Agora, isso pode não parecer muito diferente do que você tem agora, mas considere como seriam as coisas se você tivesse mais operações que poderiam resultar em uma falha:
Em cada um dos
yield
s naprocess_file
função, se a chamada da função retornar uma falha, aprocess_file
função sairá, nesse ponto , retornando o valor de falha da função com falha, em vez de continuar com o restante e retornar o valorSuccess("All ok.")
Agora, imagine fazer o acima com
if
s aninhados ! (Como você lidaria com o valor de retorno !?)Conclusão
Mônadas são legais :)
Notas:
Eu não sou um programador Python - usei a biblioteca de mônada vinculada acima em um script que eu ninja para alguma automação de projeto. Entendo, no entanto, que, em geral, a abordagem idiomática preferida é usar exceções.
IIRC, há um erro de digitação no script lib na página vinculada, embora eu esqueça onde é o caixa eletrônico. Vou atualizar se me lembrar.Eu diferenciei minha versão da página e descobri:def failable_monad_examle():
->def failable_monad_example():
- op
inexample
estava ausente.Para obter o resultado de uma função decorada disponível (como
process_file
), você deve capturar o resultado em avariable
e fazer avariable.value
para obtê-lo.fonte
Uma função é um contrato, e seu nome deve sugerir que contrato ela cumprirá. IMHO, se você o nomear,
remove_file
ele deverá remover o arquivo e , caso contrário , causará uma exceção. Por outro lado, se você nomeartry_remove_file
, ele deve "tentar" remover e retornar booleano para saber se o arquivo foi removido ou não.Isso levaria a outra pergunta - deveria ser
remove_file
outry_remove_file
? Depende do seu site de chamadas. Na verdade, você pode ter os dois métodos e usá-los em cenários diferentes, mas acho que a remoção do arquivo em si tem grandes chances de sucesso, então prefiro ter apenasremove_file
essa exceção de lançamento quando falha.fonte
Nesse caso específico, pode ser útil pensar sobre por que você talvez não consiga remover o arquivo. Digamos que o problema é que o arquivo pode ou não existir. Então você deve ter uma função
doesFileExist()
que retorne true ou false e uma funçãoremoveFile()
que exclua o arquivo.No seu código, você deve primeiro verificar se o arquivo existe. Se isso acontecer, ligue
removeFile
. Caso contrário, faça outras coisas.Nesse caso, você ainda pode querer
removeFile
lançar uma exceção se o arquivo não puder ser removido por algum outro motivo, como permissões.Para resumir, exceções devem ser lançadas para coisas que são, bem, excepcionais. Portanto, se é perfeitamente normal que o arquivo que você está tentando excluir possa não existir, isso não é uma exceção. Escreva um predicado booleano para verificar isso. Por outro lado, se você não tiver as permissões de gravação para o arquivo, ou se ele estiver em um sistema de arquivos remoto que de repente está inacessível, essas podem ser condições excepcionais.
fonte