Um método de recuperação deve retornar 'nulo' ou lançar uma exceção quando não pode produzir o valor de retorno? [fechado

StackOverflow https://stackoverflow.com/questions/175532

  •  05-07-2019
  •  | 
  •  

Pergunta

Eu tenho um método que deve devolver um objeto, se for encontrado.

Se não for encontrado, devo:

  1. retornar nulo
  2. jogue uma exceção
  3. outro
Foi útil?

Solução

Se você sempre espera encontrar um valor, jogue a exceção se estiver faltando. A exceção significaria que havia um problema.

Se o valor puder estar ausente ou presente e ambos forem válidos para a lógica do aplicativo, retorne um NULL.

Mais importante: o que você faz outros lugares no código? A consistência é importante.

Outras dicas

Ligue apenas uma exceção se for realmente um erro. Se for esperado comportamento para o objeto não existir, retorne o nulo.

Caso contrário, é uma questão de preferência.

Como regra geral, se o método sempre retornar um objeto, vá com a exceção. Se você antecipar o nulo ocasional e deseja lidar com isso de uma certa maneira, vá com o nulo.

Faça o que fizer, recomendo contra a terceira opção: retornar uma string que diz "WTF".

Se NULL nunca indicar um erro, basta retornar nulo.

Se NULL for sempre um erro, faça uma exceção.

Se NULL às vezes for uma exceção, codifique duas rotinas. Uma rotina lança uma exceção e a outra é uma rotina de teste booleana que retorna o objeto em um parâmetro de saída e a rotina retorna um falso se o objeto não foi encontrado.

É difícil usar mal uma rotina de tentativa. É muito fácil esquecer de verificar se há NULL.

Então, quando nulo é um erro que você apenas escreve

object o = FindObject();

Quando o nulo não é um erro, você pode codificar algo como

if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found

Eu só queria recapitular as opções mencionadas antes, jogando algumas novas em:

  1. retornar nulo
  2. jogue uma exceção
  3. Use o padrão de objeto nulo
  4. Forneça um parâmetro booleano para você, para que o chamador possa escolher se ele quiser que você jogue uma exceção
  5. Forneça um parâmetro extra, para que o chamador possa definir um valor que ele recupere se nenhum valor for encontrado

Ou você pode combinar estas opções:

Forneça várias versões sobrecarregadas do seu getter, para que o chamador possa decidir para qual caminho seguir. Na maioria dos casos, apenas o primeiro tem uma implementação do algoritmo de pesquisa, e os outros apenas envolvem o primeiro:

Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);

Mesmo se você optar por fornecer apenas uma implementação, convém usar uma convenção de nomenclatura como essa para esclarecer seu contrato e isso ajuda você a decidir adicionar outras implementações também.

Você não deve usá -lo demais, mas pode ser útil, especialmente ao escrever uma classe auxiliar que você usará em centenas de aplicativos diferentes com muitas convenções diferentes de manuseio de erros.

Use o padrão de objeto nulo ou faça uma exceção.

Seja consistente com as APIs que você está usando.

Basta perguntar a si mesmo: "É um caso excepcional que o objeto não seja encontrado"? Se espera -se que isso aconteça no curso normal do seu programa, você provavelmente não deve levantar uma exceção (pois não é um comportamento excepcional).

Versão curta: use exceções para lidar com um comportamento excepcional, não para lidar com o fluxo normal de controle em seu programa.

-Alan.

Depende se o seu idioma e o código promove: lbyl (procure antes de saltar) ou EAFP (mais fácil perguntar perdão do que permissão)

Lbyl diz que você deve verificar se há valores (então retorne um nulo)
O EAFP diz para apenas tentar a operação e ver se falha (jogue uma exceção)

Embora eu concorde com acima. Exceções devem ser usadas para condições excepcionais/de erro, e o retorno de um nulo é o melhor ao usar verificações.


EAFP vs. LBYL em Python:
http://mail.python.org/pipermail/python-list/2003-may/205182.html (Arquivo da Web)

Vantagens de lançar uma exceção:

  1. Fluxo de controle mais limpo no seu código de chamada. Verificar para injetar um ramo condicional nulo que é tratado nativamente por tentativa/captura. Verificar NULL não indica o que você está verificando - está checando NULL porque está procurando um erro que está esperando ou está checando NULL para que não o passasse mais no declínio ?
  2. Remove a ambiguidade do que significa "nulo". O nulo é representativo de um erro ou é nulo o que é realmente armazenado no valor? Difícil dizer quando você só tem uma coisa para basear essa determinação.
  3. Consistência aprimorada entre o comportamento do método em um aplicativo. Exceções são normalmente expostas nas assinaturas de método, para que você seja mais capaz de entender quais casos de borda os métodos em uma conta de aplicativo e quais informações o seu aplicativo pode reagir de maneira previsível.

Para mais explicações com exemplos, consulte: http://metatations.com/2011/11/17/returning-null-vs-wrowing-an-exception/

Exceções estão relacionadas ao design por contrato.

A interface de um objetos é na verdade um contrato entre dois objetos, o chamador deve cumprir o contrato ou o receptor pode simplesmente falhar com uma exceção. Existem dois contratos possíveis

1) Toda a entrada do método é válida; nesse caso, você deve retornar nulo quando o objeto não for encontrado.

2) Somente alguma entrada é válida, ou seja, o que resulta em um objeto encontrado. Nesse caso, você deve oferecer um segundo método que permita ao chamador determinar se sua entrada estará correta. Por exemplo

is_present(key)
find(key) throws Exception

Se e somente se você fornecer os dois métodos do segundo contrato, poderá fazer uma exceção não é encontrada!

Prefiro apenas devolver um nulo e confiar no chamador para lidar com isso adequadamente. A exceção (por falta de uma palavra melhor) é se eu tiver absolutamente 'certo', esse método retornará um objeto. Nesse caso, uma falha é uma excepcional deve e deve jogar.

Depende do que significa que o objeto não é encontrado.

Se for um estado de coisas normal, retorne nulo. Isso é apenas algo que pode acontecer de vez em quando, e os chamadores devem verificar.

Se for um erro, faça uma exceção, os chamadores devem decidir o que fazer com a condição de erro da falta de objeto.

Por fim, funcionaria, embora a maioria das pessoas geralmente considere uma boa prática usar apenas exceções quando algo, bem, excepcional aconteceu.

Aqui estão mais algumas sugestões.

Se devolver uma coleção, evite retornar NULL, retorne uma coleção vazia que facilita a enumeração sem uma verificação nula primeiro.

Várias APIs .NET usam o padrão de um parâmetro ThrownONerror, que dá ao chamador a escolha como é realmente uma situação excepcional ou não se o objeto não foi encontrado. Type.gettype é um exemplo disso. Outro padrão comum com o BCL é o padrão de tryget em que um booleano é retornado e o valor é passado por meio de um parâmetro de saída.

Você também pode considerar o padrão de objeto nulo em algumas circunstâncias que podem ser um padrão ou uma versão sem comportamento. A chave é evitar verificações nulas em toda a base de código. Veja aqui para mais informações http://geekswithblogs.net/dsellers/archive/2006/09/08/90656.aspx

Em algumas funções, adiciono um parâmetro:

..., bool verify = true)

Verdadeiro significa arremesso, false significa retornar algum valor de retorno de erro. Dessa forma, quem usa essa função tem as duas opções. O padrão deve ser verdadeiro, para o benefício daqueles que esquecem o tratamento de erros.

Retorne um nulo em vez de jogar uma exceção e documentar claramente a possibilidade de um valor de retorno nulo na documentação da API. Se o código de chamada não honrar a API e verificar o caso nulo, provavelmente resultará em algum tipo de "exceção do ponteiro nulo" de qualquer maneira :)

Em C ++, posso pensar em três sabores diferentes de configurar um método que encontra um objeto.

Opção a

Object *findObject(Key &key);

Retorne nulo quando um objeto não puder ser encontrado. Bom e simples. Eu iria com este. As abordagens alternativas abaixo são para pessoas que não odeiam as partes.

Opção b

void findObject(Key &key, Object &found);

Passe em uma referência à variável que receberá o objeto. O método lançou uma exceção quando um objeto não pode ser encontrado. Esta convenção é provavelmente mais adequada se não for realmente esperado para que um objeto não seja encontrado - portanto, você faz uma exceção para significar que é um caso inesperado.

Opção c

bool findObject(Key &key, Object &found);

O método retorna false quando um objeto não pode ser encontrado. A vantagem disso sobre a opção A é que você pode verificar o caso de erro em uma etapa clara:

if (!findObject(myKey, myObj)) { ...

Referindo -se apenas ao caso em que Null não é considerado um comportamento excepcional que eu sou definitivamente para o método de tentativa, é claro, não há necessidade de "ler o livro" ou "olhar antes de pular", como foi dito aqui

então, basicamente:

bool TryFindObject(RequestParam request, out ResponseParam response)

E isso significa que o código do usuário também ficará claro

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...

Se é importante que o código do cliente saiba a diferença entre encontrado e não encontrado e esse deve ser um comportamento de rotina, é melhor retornar nulo. O código do cliente pode decidir o que fazer.

Geralmente deve retornar nulo. O código que chama o método deve decidir se deve lançar uma exceção ou tentar outra coisa.

Ou devolver uma opção

Uma opção é basicamente uma classe de contêineres que força o cliente a lidar com casos de estande. Scala tem esse conceito, procure sua API.

Em seguida, você tem métodos como T GetorElse (T ValueIfNull) neste objeto, o retorno do objeto encontrado ou um alternativo o cliente especifica.

Infelizmente, o JDK é inconsistente, se você está tentando acessar a chave não existente no pacote de recursos, você não é encontrado exceção e quando solicita valor do mapa, fica nulo se não existir. Então, eu mudaria a resposta do vencedor para o seguinte, se o valor encontrado puder ser nulo, aumentaria a exceção quando não for encontrada, caso contrário, retorne nulo. Portanto, siga a regra com uma exceção, se você precisar saber por que o valor não é encontrado, sempre aumente a exceção, ou ..

Contanto que devolva um referência Para o objeto, retornar um nulo deve ser bom.

No entanto, se está retornando a coisa toda sangrenta (como em C ++ se você fizer: 'Retorne blá;' em vez de 'retornar e blá;' (ou 'blá' é um ponteiro), então você não pode retornar um nulo, porque é não do tipo 'objeto'. Nesse caso, lançar uma exceção ou retornar um objeto em branco que não tem um conjunto de sinalizadores de sucesso é como eu abordaria o problema.

Não pense em alguém mencionado a sobrecarga no tratamento de exceções - leva recursos adicionais para carregar e processar a exceção, portanto, a menos que seja um verdadeiro evento de matança de aplicativos ou parada de processos (daqui para frente causaria mais mal do que bem), eu optaria por despachar um de volta Valor O ambiente de chamada pode interpretar como achar adequado.

Concordo com o que parece ser o consenso aqui (retorne nulo se "não encontrado" é um resultado possível normal ou faça uma exceção se a semântica da situação exigir que o objeto sempre seja encontrado).

Há, no entanto, uma terceira possibilidade que possa fazer sentido, dependendo da sua situação específica. Seu método pode retornar um objeto padrão de algum tipo na condição "não encontrada", permitindo que o código de chamada tenha certeza de que ele sempre receberá um objeto válido sem a necessidade de verificação nula ou exceção.

Retorne uma NULL, exceções são exatamente isso: algo que seu código faz que não é esperado.

Exceções devem ser excepcional. Retornar nulo Se for válido devolver um nulo.

Prefira retornar nulo -

Se o chamador o usar sem verificar, a exceção acontecerá ali de qualquer maneira.

Se o interlocutor realmente não o usar, não o tribute um try/catch quadra

Se o método retornar uma coleção, retorne uma coleção vazia (como Sayed acima). Mas por favor, não coleções.Empty_List ou algo assim! (No caso de Java)

Se o método recuperar um único objeto, você terá algumas opções.

  1. Se o método sempre encontrar o resultado e é um caso de exceção real para não encontrar o objeto, você deve lançar uma exceção (em Java: por favor, uma exceção desmarcada)
  2. (Somente Java) Se você pode tolerar que o método lança uma exceção verificada, jogue um projeto específico do objectNotFoundException ou similar. Nesse caso, o compilador diz que você se esquecer de lidar com a exceção. (Este é o meu manuseio preferido de coisas não encontradas em Java.)
  3. Se você disser que está realmente bem, se o objeto não for encontrado e o nome do seu método é como o FindBookForauthororreReRTurnNull (..), você poderá retornar nulo. Nesse caso, é fortemente Recomendado para usar algum tipo de verificação estática ou verificação do compilador, o que impede a desreferência do resultado sem uma verificação nula. No caso de Java, pode ser por exemplo. FindBugs (ver DefaultAnnotation em http://findbugs.sourceforge.net/manual/annotações.html) ou verificação de Intellij.

Tenha cuidado, se você decidir devolver um nulo. Se você não é o único programador no projeto, obterá o NullPointerExceptions (em Java ou qualquer outra coisa em outros idiomas) no tempo de execução! Portanto, não devolva nulos que não são verificados no momento da compilação.

Se você está usando uma biblioteca ou outra classe que lança uma exceção, você deve RETHROW isto. Aqui está um exemplo. Exemplo2.java é como biblioteca e exemplo.java usa seu objeto. Main.java é um exemplo para lidar com essa exceção. Você deve mostrar uma mensagem significativa e (se necessário) rastreamento de empilhamento para o usuário no lado da chamada.

Main.java

public class Main {
public static void main(String[] args) {
    Example example = new Example();

    try {
        Example2 obj = example.doExample();

        if(obj == null){
            System.out.println("Hey object is null!");
        }
    } catch (Exception e) {
        System.out.println("Congratulations, you caught the exception!");
        System.out.println("Here is stack trace:");
        e.printStackTrace();
    }
}
}

Exemplo.java

/**
 * Example.java
 * @author Seval
 * @date 10/22/2014
 */
public class Example {
    /**
     * Returns Example2 object
     * If there is no Example2 object, throws exception
     * 
     * @return obj Example2
     * @throws Exception
     */
    public Example2 doExample() throws Exception {
        try {
            // Get the object
            Example2 obj = new Example2();

            return obj;

        } catch (Exception e) {
            // Log the exception and rethrow
            // Log.logException(e);
            throw e;
        }

    }
}

Exemplo2.Java

 /**
 * Example2.java
 * @author Seval
 *
 */
public class Example2 {
    /**
     * Constructor of Example2
     * @throws Exception
     */
    public Example2() throws Exception{
        throw new Exception("Please set the \"obj\"");
    }

}

Isso realmente depende se você espera encontrar o objeto, ou não. Se você seguir a escola de pensamento de que as exceções devem ser usadas para indicar algo, bem, err, excepcional ocorreu então:

  • Objeto encontrado; objeto de retorno
  • Objeto não encontrado; lançar exceção

Caso contrário, retorne nulo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top