Como nomear um método que executa uma tarefa e retorna um booleano como status?


33

Se houver um método

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

deveria ser chamado IsStuffDone()?

Ambos os nomes podem ser mal interpretados pelo usuário: se o nome é DoStuff()por que ele retorna um valor booleano? Se o nome for IsStuffDone(), não está claro se o método executa uma tarefa ou apenas verifica seu resultado.

Existe uma convenção para este caso? Ou uma abordagem alternativa, como esta é considerada defeituosa? Por exemplo, em idiomas que possuem parâmetros de saída, como C #, uma variável de status booleana pode ser passada para o método como uma e o tipo de retorno do método void.

EDIT: No meu problema particular, o tratamento de exceções não pode ser delegado diretamente ao chamador, porque o método faz parte de uma implementação de interface. Portanto, o chamador não pode ser acusado de lidar com todas as exceções de diferentes implementações. Não está familiarizado com essas exceções. No entanto, o chamador pode lidar com uma exceção personalizada, como StuffHasNotBeenDoneForSomeReasonExceptionsugerido na resposta e no comentário do npinti .


2
Depende do uso da função e do material que está sendo feito. Se for necessário, que o material precise ser executado corretamente, consideraria esta abordagem falha, porque o usuário da função pode perder o sinalizador booleano e também não possui as informações fornecidas pela exceção.
Benni

7
Na maioria das vezes, eu chamaria isso de "quebrado", pois retornar um em booleanvez de quebrar ou passar a exceção quase sempre está errado.
Maaartinus

2
Esse tipo de manipulador de exceção raramente é uma boa idéia. Capture apenas exceções específicas que você espera, nem todas. E, se possível, evite jogá-lo em primeiro lugar, eliminando a necessidade de pegá-lo.
#

2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? E para responder à sua pergunta sobre as exceções - eu deixaria o chamador lidar com elas ou as capturaria e, em seguida, lançaria uma exceção personalizada que significa "esse método não funciona". Compartilhe e curta.
Bob Jarvis - Restabelece Monica

5
Para todos aqueles que estão dizendo: basta lançar (ou deixar passar) uma exceção, você está fazendo suposições infundadas sobre como esse código está sendo usado. Um cenário possível é que existe um problema a ser resolvido, com vários métodos de solução heurística que resolvem subclasses cada vez maiores do problema a um custo cada vez maior; faria sentido escrever algo parecido if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Fazer o mesmo com exceções (personalizadas?) Seria inutilmente mais complicado.
Marc van Leeuwen 12/06

Respostas:


68

No .NET, você geralmente possui pares de métodos em que um deles pode gerar uma exceção ( DoStuff) e o outro retorna um status booleano e, em execução bem-sucedida, o resultado real por meio de um parâmetro out ( TryDoStuff).

(A Microsoft chama isso de "Padrão Try-Parse" , pois talvez o exemplo mais proeminente seja o TryParsemétodo de vários tipos primitivos.)

Se o Tryprefixo é incomum no seu idioma, você provavelmente não deve usá-lo.


3
+1 É assim que eu vi esse tipo de coisa acontecer em outro lugar. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");é um idioma bastante padrão e intuitivo na minha opinião.
9134 Karl Nicoll

12
Deve-se observar que TryDoStuffse supõe que os métodos falham de maneira limpa e não têm efeito colateral quando retornam falsos.
precisa

Para sua informação, também existem Removemétodos, por exemplo, na estrutura .NET que removerão um elemento de uma estrutura de dados (por exemplo Dictionary) e retornarão a bool. O consumidor da API pode decidir consumi-lo ou ignorá-lo. No entanto, os erros são relatados como exceções.
Omer Iqbal

18

E se você simplesmente lançou a exceção no código de chamada?

Dessa forma, você está delegando o tratamento de exceções para quem estiver usando seu código. E se, no futuro, você desejar fazer o seguinte:

  • Se nenhuma exceção for lançada, execute a ação A
  • Se (por exemplo) a FileNotFoundExceptionfor lançada, execute a ação B
  • Se qualquer outra exceção for lançada, execute a ação C

Se você retroceder sua exceção, a alteração acima implicaria simplesmente a adição de um catchbloco extra . Se você deixar como está, precisará alterar o método e o local a partir do qual o método é chamado, que, dependendo da complexidade do projeto, pode estar em vários locais.


1
E se a exceção for do tipo que não pode ser delegada e deve ser tratada internamente?
Limbo Exile

2
@LimboExile: Eu acho que você ainda pode lidar com isso internamente e, em seguida, talvez lançar outra exceção, talvez a sua. Isso ilustraria que algo que não deveria ter acontecido ocorreu, mas, ao mesmo tempo, você não expôs o que realmente está acontecendo sob o capô, o que eu assumo é por que você deseja lidar com a exceção internamente.
Npinti

6
Talvez valha a pena ter em mente que lançar uma exceção deve ser para um caso excepcional . Se for comum ou normal DoStufffalhar, lançar uma exceção para o caso de falha seria semelhante ao controle do fluxo do programa com exceções ruins. Exceções também têm um custo de desempenho inerente. Se DoStufffalhar é um caso incomum devido a um erro, as exceções definitivamente são o caminho a seguir, como sugere o @npinti.
Karl Nicoll

1
@KarlNicoll Se algo é "excepcional" é completamente subjetivo. O principal critério deve ser se você deseja que o programa falhe se ninguém fizer algo sobre o erro e se você deseja que a possibilidade de erro esteja presente no tipo de função. Além disso, não é fato que as exceções sejam caras; depende do idioma e de como você os joga. As exceções são baratas no Python, e se você extrair as informações de rastreamento da pilha, elas também podem ser baratas no Java. Mesmo que houvesse um custo de desempenho, é uma otimização prematura se preocupar com isso até você criar um perfil.
Doval

1
@Doval - Concordo que é subjetivo, razão pela qual o OP não deve desconsiderar totalmente a resposta da npinti, pois poderia muito bem ser o melhor curso de ação. Meu argumento era que lançar exceções nem sempre é o melhor caminho a seguir. Existem muitos casos em que uma falha não justifica lançar uma exceção e potencialmente travar um aplicativo. Por exemplo, se o DoStuff()método do OP limpar após o código interno gerar uma exceção, e as Pós-condições do método ainda estiverem corretas, um código de retorno poderá ser uma opção melhor, pois o erro foi tratado.
Karl Nicoll

7

DoStuff() é suficiente, e o valor retornado da função deve ser documentado, e você não precisa mencioná-lo no nome da função, procurando muitas API disponíveis:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )

6

Em Java, a API Collection define um método add que retorna um booleano. Basicamente, retorna se a adição alterou a coleção. Portanto, para um, Listele geralmente retornará verdadeiro, pois o item foi adicionado, enquanto para um Setpode retornar falso, se o item já estiver lá, pois Sets permite cada item único no máximo uma vez.

Dito isto, se você adicionar um item que não é permitido pela coleção, por exemplo, um valor nulo, a adição lançará um em NullPointerExceptionvez de retornar falso.

Quando você aplica a mesma lógica ao seu caso, por que você precisa retornar um booleano? Se é para ocultar uma exceção, não. Basta lançar a exceção. Dessa forma, você pode ver o que deu errado. Se você precisar de um booleano, porque talvez não tenha sido feito, por algum outro motivo que não seja uma exceção, nomeie o método DoStuff(), pois isso representa o comportamento. Faz coisas.


1

Pode falhar por diferentes razões, Exceção, condições normais, então usaria isso:

boolean doStuff() - returns true when successful

Importante é documentar o que o booleano significa.


1

Normalmente, tenho métodos que retornam um OperationResult(ou OperationResult<T>) e definem a IsSuccesspropriedade OperationResultadequadamente. Se o método 'usual' for nulo, retorne OperationResult, se o método 'usual' retornar um objeto, retorne OperationResult<T>e defina a Itempropriedade OperationResultadequadamente. Eu também sugeriria ter métodos OperationResultcomo .Failed(string reason)e .Succeeded(T item). OperationResultrapidamente se torna um tipo familiar em seus sistemas e os desenvolvedores sabem como lidar com isso.


0

Realmente depende do idioma que você está usando. Como em alguns idiomas, existem convenções e protocolos que realmente não existem em muitos idiomas ou em nenhum deles.

Por exemplo, na linguagem Objective-C e no iOS / Mac OS X SDK, os nomes dos métodos geralmente seguem as seguintes linhas:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Como você pode ver, existe um método chamado viewDidLoad. Prefiro usar esse tipo de nomeação sempre que criar meus próprios aplicativos. Isso pode ser usado para verificar se algo deveria acontecer como você disse. Acredito que você esteja pensando profundamente sobre o que é o nome de seus métodos. A maioria dos métodos retorna o status do que eles fazem independentemente, por exemplo:

No PHP, mysql_connect () retornará false se a conexão falhar. Não diz que sim, mas sim, e a documentação diz que sim, para que não possa ser mal interpretada pelo programador.


Ah, mas viewDidLoad é mais uma notificação de retorno de chamada. Os usuários não ligam para carregar a exibição. Em vez disso, é chamado depois que a exibição é carregada. Da mesma forma: isVisible não define a visibilidade, mas apenas retorna o valor atual. Ele não faz nada.
lilbyrdie

0

Gostaria de sugerir o uso do prefixo 'Try'. Esse é um padrão bem conhecido para situações em que o método pode ter êxito ou não. Esse padrão de nomenclatura foi usado pela Microsoft no .NET Framework (por exemplo, TryParseInt).

Mas, talvez isso não seja sobre nomeação. É sobre como você projeta os parâmetros de entrada / saída do seu método.

Minha idéia é que, se um método tiver um valor de retorno, o objetivo de chamar esse método deve ser obter esse valor de retorno. Portanto, evite usar o tipo de retorno para indicar o status de uma operação.

Em C #, prefiro usar um parâmetro out. Usando uma saída, estou marcando o parâmetro como um parâmetro lateral. Especialmente que o C # 6 oferecerá suporte à declaração de variável embutida.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.

0

Eu escolheria um nome com base no papel principal do método. O fato de esse método retornar valor booleano não exige o prefixo 'Is'. Vamos ter um código Java, que usa o prefixo 'Is' bastante extensivamente. Dê uma olhada java.io.File.delete(). Ele retorna boolean, mas não é chamado isFileDeleted(). O motivo é que a principal função do método é excluir um arquivo. Alguém chama esse método com a intenção de excluir um arquivo.

O booleano existe apenas para comunicar de volta o resultado do trabalho. Por outro lado, vamos verjava.util.Map.isEmpty() . Obviamente, você chama esse método para descobrir se a coleção está vazia. Você não quer que nada seja feito. Você só quer descobrir qual é o estado atual.

Então, pessoalmente, eu definitivamente ficaria DoStuff().

Esta é uma questão muito importante para mim. Muitas vezes, quando mantenho o código legado, deparo-me com algo que chamo pessoalmente de "método da mentira". O nome sugere a ação A, mas o corpo executa a ação B. O exemplo mais interessante é o método getter, que altera os dados no banco de dados.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.