Pergunta

Tenho exceções criadas para todas as condições que meu aplicativo não espera. UserNameNotValidException, PasswordNotCorrectException etc.

No entanto, disseram-me que não deveria criar exceções para essas condições.Na minha UML, essas SÃO exceções ao fluxo principal, então por que não deveria ser uma exceção?

Alguma orientação ou práticas recomendadas para criar exceções?

Foi útil?

Solução

Minha orientação pessoal é:uma exceção é lançada quando uma suposição fundamental do bloco de código atual é considerada falsa.

Exemplo 1:digamos que eu tenha uma função que deveria examinar uma classe arbitrária e retornar verdadeiro se essa classe herdar de List<>.Esta função faz a pergunta: "Esse objeto é descendente da lista?" Essa função nunca deve lançar uma exceção, porque não há áreas cinzentas em sua operação - todas as classes herdam ou não da lista <>, então a resposta é sempre "sim" ou "não".

Exemplo 2:digamos que tenho outra função que examina List<> e retorna verdadeiro se seu comprimento for maior que 50 e falso se o comprimento for menor.Esta função faz a pergunta: "Esta lista tem mais de 50 itens?" Mas essa pergunta faz uma suposição - assume que o objeto que é dado é uma lista.Se eu entregar NULL, essa suposição é falsa.Nesse caso, se a função retornar qualquer verdadeiro ou falso, então está quebrando suas próprias regras.A função não pode retornar qualquer coisa e afirmar que respondeu à pergunta corretamente.Portanto, não retorna - lança uma exceção.

Isto é comparável ao "pergunta carregada" falácia lógica.Cada função faz uma pergunta.Se a entrada fornecida tornar essa pergunta uma falácia, lance uma exceção.Essa linha é mais difícil de desenhar com funções que retornam void, mas o resultado final é:se as suposições da função sobre suas entradas forem violadas, ela deverá lançar uma exceção em vez de retornar normalmente.

O outro lado desta equação é:se você perceber que suas funções lançam exceções com frequência, provavelmente precisará refinar suas suposições.

Outras dicas

Porque são coisas que vão acontecer normalmente.As exceções não são mecanismos de controle de fluxo.Os usuários muitas vezes erram as senhas, não é um caso excepcional.Exceções deveriam ser algo verdadeiramente raro, UserHasDiedAtKeyboard situações do tipo.

Minhas pequenas orientações são fortemente influenciadas pelo grande livro "Code complete":

  • Use exceções para notificar sobre coisas que não devem ser ignoradas.
  • Não use exceções se o erro puder ser tratado localmente
  • Certifique-se de que as exceções estejam no mesmo nível de abstração do restante da sua rotina.
  • As exceções devem ser reservadas para o que é verdadeiramente excepcional.

NÃO é uma exceção se o nome de usuário não for válido ou a senha não estiver correta.Essas são coisas que você deve esperar no fluxo normal de operação.Exceções são coisas que não fazem parte da operação normal do programa e são bastante raras.

EDITAR:Não gosto de usar exceções porque você não consegue saber se um método lança uma exceção apenas olhando a chamada.É por isso que as exceções só devem ser usadas se você não conseguir lidar com a situação de maneira decente (pense "sem memória" ou "o computador está pegando fogo").

Uma regra prática é usar exceções no caso de algo que você normalmente não poderia prever.Exemplos são conectividade de banco de dados, arquivo ausente no disco, etc.Para cenários que você pode prever, ou seja, usuários tentando fazer login com uma senha incorreta, você deve usar funções que retornem booleanos e saibam como lidar com a situação normalmente.Você não deseja encerrar abruptamente a execução lançando uma exceção só porque alguém digitou a senha incorretamente.

Outros propõem que exceções não devem ser usadas porque o login incorreto é esperado em um fluxo normal se o usuário digitar incorretamente.Discordo e não entendo o raciocínio.Compare isso com a abertura de um arquivo.se o arquivo não existir ou não estiver disponível por algum motivo, uma exceção será lançada pela estrutura.Usando a lógica acima, isso foi um erro da Microsoft.Eles deveriam ter retornado um código de erro.O mesmo para análise, webrequests, etc., etc.

Não considero um login incorreto como parte de um fluxo normal, é excepcional.Normalmente o usuário digita a senha correta e o arquivo existe.Os casos excepcionais são excepcionais e é perfeitamente aceitável usar exceções para eles.Complicar seu código propagando valores de retorno através de n níveis acima da pilha é um desperdício de energia e resultará em um código confuso.Faça a coisa mais simples que poderia funcionar.Não otimize prematuramente usando códigos de erro; coisas excepcionais, por definição, raramente acontecem e as exceções não custam nada, a menos que você as lance.

As exceções são um efeito um tanto caro; se, por exemplo, você tiver um usuário que fornece uma senha inválida, normalmente é melhor repassar um sinalizador de falha ou algum outro indicador de que ela é inválida.

Isso se deve à maneira como as exceções são tratadas, entradas realmente incorretas e itens de parada crítica exclusivos devem ser exceções, mas não informações de login com falha.

Acho que você só deve lançar uma exceção quando não houver nada que possa fazer para sair do seu estado atual.Por exemplo, se você estiver alocando memória e não houver nenhuma para alocar.Nos casos mencionados, você pode se recuperar claramente desses estados e retornar um código de erro ao chamador de acordo.


Você verá muitos conselhos, inclusive nas respostas a esta pergunta, de que deve lançar exceções apenas em circunstâncias "excepcionais".Isso parece superficialmente razoável, mas é um conselho falho, porque substitui uma pergunta (“quando devo lançar uma exceção”) por outra questão subjetiva (“o que é excepcional”).Em vez disso, siga o conselho de Herb Sutter (para C++, disponível no Artigo do Dr. Dobbs Quando e como usar exceções, e também em seu livro com Andrei Alexandrescu, Padrões de codificação C++):lançar uma exceção se, e somente se

  • uma pré -condição não é atendida (que normalmente torna um dos seguintes impossíveis) ou
  • a alternativa não conseguiria satisfazer uma pós-condição ou
  • a alternativa não conseguiria manter um invariante.

Por que isso é melhor?Isso não substitui a pergunta por diversos perguntas sobre pré-condições, pós-condições e invariantes?Isso é melhor por vários motivos relacionados.

  • Pré-condições, pós-condições e invariantes são projeto características do nosso programa (sua API interna), enquanto a decisão de throw é um detalhe de implementação.Isso nos obriga a ter em mente que devemos considerar o design e sua implementação separadamente, e nosso trabalho ao implementar um método é produzir algo que satisfaça as restrições do design.
  • Isso nos força a pensar em termos de pré-condições, pós-condições e invariantes, que são as apenas suposições que os chamadores do nosso método devem fazer e são expressas com precisão, permitindo um acoplamento fraco entre os componentes do nosso programa.
  • Esse acoplamento fraco nos permite refatorar a implementação, se necessário.
  • As pós-condições e invariantes são testáveis;resulta em um código que pode ser facilmente testado em unidade, porque as pós-condições são predicados que nosso código de teste de unidade pode verificar (afirmar).
  • Pensar em termos de pós-condições produz naturalmente um design que tem sucesso como uma pós-condição, que é o estilo natural para usar exceções.O caminho de execução normal ("feliz") do seu programa é apresentado linearmente, com todo o código de tratamento de erros movido para o catch cláusulas.

Eu diria que não existem regras rígidas e rápidas sobre quando usar exceções.No entanto, existem boas razões para usá-los ou não:

Razões para usar exceções:

  • O fluxo de código para o caso comum é mais claro
  • Pode retornar informações de erro complexas como um objeto (embora isso também possa ser alcançado usando o parâmetro de erro "out" passado por referência)
  • As linguagens geralmente fornecem alguma facilidade para gerenciar a limpeza organizada no caso de exceção (tente/finalmente em Java, usando em C#, RAII em C++)
  • Caso nenhuma exceção seja lançada, a execução pode às vezes ser mais rápido do que verificar códigos de retorno
  • Em Java, as exceções verificadas devem ser declaradas ou capturadas (embora isso possa ser uma razão contra)

Razões para não usar exceções:

  • Às vezes é um exagero se o tratamento de erros for simples
  • Se as exceções não forem documentadas ou declaradas, elas poderão não ser detectadas pelo código de chamada, o que pode ser pior do que se o código de chamada simplesmente ignorasse um código de retorno (saída do aplicativo vs falha silenciosa - o que é pior pode depender do cenário)
  • Em C++, o código que usa exceções deve ser seguro contra exceções (mesmo que você não as lance ou capture, mas chame uma função de lançamento indiretamente)
  • Em C++, é difícil dizer quando uma função pode ser lançada; portanto, você deve ficar paranóico com a segurança de exceções se usá-las
  • Lançar e capturar exceções geralmente é significativamente mais caro em comparação com a verificação de um sinalizador de retorno

Em geral, eu estaria mais inclinado a usar exceções em Java do que em C++ ou C#, porque sou da opinião que uma exceção, declarada ou não, é fundamentalmente parte da interface formal de uma função, uma vez que alterar sua garantia de exceção pode quebrar o código de chamada.A maior vantagem de usá-los em Java IMO é que você sabe que seu chamador DEVE lidar com a exceção, e isso aumenta a chance de comportamento correto.

Por causa disso, em qualquer linguagem, eu sempre derivaria todas as exceções em uma camada de código ou API de uma classe comum, de modo que o código de chamada sempre possa garantir a captura de todas as exceções.Além disso, consideraria ruim lançar classes de exceção específicas da implementação ao escrever uma API ou biblioteca (ou seja,agrupar exceções de camadas inferiores para que a exceção que seu chamador recebe seja compreensível no contexto de sua interface).

Observe que Java faz a distinção entre exceções gerais e de tempo de execução, pois as últimas não precisam ser declaradas.Eu só usaria classes de exceção de tempo de execução quando você soubesse que o erro é resultado de um bug no programa.

As classes de exceção são como classes "normais".Você cria uma nova classe quando ela "é" um tipo diferente de objeto, com campos e operações diferentes.

Como regra geral, você deve tentar equilibrar o número de exceções e a granularidade das exceções.Se o seu método lançar mais de 4 a 5 exceções diferentes, você provavelmente poderá mesclar algumas delas em exceções mais "gerais" (por exemplo,no seu caso, "AuthenticationFailedException") e usando a mensagem de exceção para detalhar o que deu errado.A menos que seu código trate cada um deles de maneira diferente, você não precisará criar muitas classes de exceção.E se isso acontecer, você deve apenas retornar um enum com o erro que ocorreu.É um pouco mais limpo assim.

Se for um código executado dentro de um loop que provavelmente causará uma exceção repetidas vezes, lançar exceções não é uma coisa boa, porque elas são muito lentas para N grandes.Mas não há nada de errado em lançar exceções personalizadas se o desempenho não for um problema.Apenas certifique-se de ter uma exceção base que todos eles herdam, chamada BaseException ou algo parecido.BaseException herda System.Exception, mas todas as suas exceções herdam BaseException.Você pode até ter uma árvore de tipos de Exception para agrupar tipos semelhantes, mas isso pode ou não ser um exagero.

Portanto, a resposta curta é que, se isso não causar uma penalidade significativa no desempenho (o que não deveria acontecer, a menos que você esteja lançando muitas exceções), vá em frente.

Eu concordo com Japollock lá em cima - aceite quando não tiver certeza sobre o resultado de uma operação.Chamadas para APIs, acesso a sistemas de arquivos, chamadas de banco de dados, etc.Sempre que você ultrapassar os "limites" de suas linguagens de programação.

Gostaria de acrescentar que fique à vontade para lançar uma exceção padrão.A menos que você faça algo "diferente" (ignorar, enviar e-mail, registrar, mostrar aquela imagem de baleia do Twitter, etc.), não se preocupe com exceções personalizadas.

a regra prática para lançar exceções é bastante simples.você faz isso quando seu código entra em um estado UNRECOVERABLE INVALID.se os dados estiverem comprometidos ou você não conseguir reverter o processamento que ocorreu até o ponto, você deverá encerrá-lo.na verdade, o que mais você pode fazer?sua lógica de processamento eventualmente falhará em outro lugar.se você puder se recuperar de alguma forma, faça isso e não lance exceção.

no seu caso específico, se você foi forçado a fazer algo bobo, como aceitar a retirada de dinheiro e só então verificar o usuário/senha, você deve encerrar o processo lançando uma exceção para notificar que algo ruim aconteceu e evitar maiores danos.

Em geral você quer lançar uma exceção para qualquer coisa que possa acontecer na sua aplicação que seja "Excepcional"

No seu exemplo, ambas as exceções parecem que você as está chamando por meio de uma validação de senha/nome de usuário.Nesse caso, pode-se argumentar que não é realmente excepcional que alguém digite incorretamente um nome de usuário/senha.

Eles são “exceções” ao fluxo principal da sua UML, mas são mais “ramificações” no processamento.

Se você tentou acessar seu arquivo ou banco de dados passwd e não conseguiu, isso seria um caso excepcional e justificaria o lançamento de uma exceção.

Em primeiro lugar, se os usuários da sua API não estão interessados ​​em falhas específicas e refinadas, então ter exceções específicas para eles não tem nenhum valor.

Como muitas vezes não é possível saber o que pode ser útil para seus usuários, uma abordagem melhor é ter exceções específicas, mas garantir que elas herdem de uma classe comum (por exemplo, std::exception ou seus derivados em C++).Isso permite que seu cliente capture exceções específicas, se assim o desejar, ou a exceção mais geral, se não se importar.

As exceções destinam-se a eventos que são comportamentos anormais, erros, falhas e outros.Comportamento funcional, erro do usuário, etc., devem ser tratados pela lógica do programa.Como uma conta ou senha incorreta é uma parte esperada do fluxo lógico em uma rotina de login, ela deve ser capaz de lidar com essas situações sem exceções.

Tenho problemas filosóficos com o uso de exceções.Basicamente, você espera que um cenário específico ocorra, mas, em vez de lidar com isso explicitamente, está pressionando o problema a ser tratado "em outros lugares". E onde isso "em outros lugares" pode ser um palpite de alguém.

Eu diria que geralmente todo fundamentalismo leva ao inferno.

Você certamente não gostaria de acabar com um fluxo orientado a exceções, mas evitar exceções também é uma má ideia.Você tem que encontrar um equilíbrio entre as duas abordagens.O que eu não faria é criar um tipo de exceção para cada situação excepcional.Isso não é produtivo.

O que geralmente prefiro é criar dois tipos básicos de exceções que são usadas em todo o sistema: Exceção Lógica e Exceção Técnica.Eles podem ser diferenciados por subtipos, se necessário, mas geralmente não é necessário.

A exceção técnica denota a exceção realmente inesperada, como o servidor de banco de dados inativo, a conexão com o serviço da web gerou o IOException e assim por diante.

Por outro lado, as exceções lógicas são usadas para propagar a situação errônea menos grave para as camadas superiores (geralmente algum resultado de validação).

Observe que mesmo a exceção lógica não se destina a ser usada regularmente para controlar o fluxo do programa, mas sim para destacar a situação em que o fluxo realmente deveria terminar.Quando usados ​​em Java, ambos os tipos de exceção são Exceção de tempo de execução subclasses e tratamento de erros são altamente orientados a aspectos.

Portanto, no exemplo de login, pode ser aconselhável criar algo como AuthenticationException e distinguir as situações concretas por valores enum como Nome de usuário não existente, Senha incorreta etc.Então você não terá uma enorme hierarquia de exceções e poderá manter os blocos catch em um nível sustentável.Você também pode empregar facilmente algum mecanismo genérico de tratamento de exceções, já que você tem as exceções categorizadas e sabe muito bem o que propagar para o usuário e como.

Nosso uso típico é lançar LogicalException durante a chamada do serviço Web quando a entrada do usuário é inválida.A exceção é empacotada para o detalhe SOAPFault e, em seguida, desempacotada para a exceção novamente no cliente, o que resulta na exibição do erro de validação em um determinado campo de entrada da página da Web, uma vez que a exceção tem mapeamento adequado para esse campo.

Esta certamente não é a única situação:você não precisa acessar o serviço da web para gerar a exceção.Você é livre para fazer isso em qualquer situação excepcional (como no caso de precisar falhar rapidamente) - tudo fica a seu critério.

o principal motivo para evitar o lançamento de uma exceção é que há muita sobrecarga envolvida no lançamento de uma exceção.

Uma coisa que o artigo abaixo afirma é que uma exceção é para condições e erros excepcionais.

Um nome de usuário errado não é necessariamente um erro de programa, mas um erro de usuário...

Aqui está um ponto de partida decente para exceções no .NET:http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

O lançamento de exceções faz com que a pilha se desenrole, o que tem alguns impactos no desempenho (é verdade que os ambientes gerenciados modernos melhoraram isso).Ainda lançar e capturar exceções repetidamente em uma situação aninhada seria uma má ideia.

Provavelmente mais importante do que isso, as exceções destinam-se a condições excepcionais.Eles não devem ser usados ​​para fluxo de controle comum, pois isso prejudicará a legibilidade do seu código.

Eu tenho três tipos de condições que pego.

  1. Entrada incorreta ou ausente não deve ser uma exceção.Use js do lado do cliente e regex do lado do servidor para detectar, definir atributos e encaminhar de volta para a mesma página com mensagens.

  2. A AppException.Geralmente, essa é uma exceção que você detecta e lança em seu código.Em outras palavras, estes são os que você espera (o arquivo não existe).Registre-o, defina a mensagem e retorne à página de erro geral.Esta página geralmente contém algumas informações sobre o que aconteceu.

  3. A exceção inesperada.Esses são aqueles que você não conhece.Registre-o com detalhes e encaminhe-os para uma página de erro geral.

Espero que isto ajude

A segurança está associada ao seu exemplo:Você não deve dizer a um invasor que existe um nome de usuário, mas que a senha está errada.Essas são informações extras que você não precisa compartilhar.Basta dizer "o nome de usuário ou a senha estão incorretos".

A resposta simples é: sempre que uma operação for impossível (por causa de um aplicativo OU porque violaria a lógica de negócios).Se um método for invocado e for impossível fazer o que o método foi escrito para fazer, lance uma exceção.Um bom exemplo é que os construtores sempre lançam ArgumentExceptions se uma instância não puder ser criada usando os parâmetros fornecidos.Outro exemplo é InvalidOperationException, que é lançada quando uma operação não pode ser executada devido ao estado de outro membro ou membros da classe.

No seu caso, se um método como Login(username, password) for invocado, se o nome de usuário não for válido, é realmente correto lançar uma UserNameNotValidException ou PasswordNotCorrectException se a senha estiver incorreta.O usuário não pode fazer login usando os parâmetros fornecidos (ou seja,é impossível porque violaria a autenticação), então lance uma exceção.Embora eu possa ter suas duas exceções herdadas de ArgumentException.

Dito isto, se você NÃO deseja lançar uma exceção porque uma falha de login pode ser muito comum, uma estratégia é criar um método que retorne tipos que representem falhas diferentes.Aqui está um exemplo:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

A maioria dos desenvolvedores é ensinada a evitar exceções devido à sobrecarga causada por lançá-las.É ótimo se preocupar com os recursos, mas geralmente não às custas do design do seu aplicativo.Essa é provavelmente a razão pela qual você foi informado para não lançar suas duas exceções.Usar exceções ou não geralmente se resume à frequência com que a exceção ocorrerá.Se for um resultado bastante comum ou bastante esperado, é quando a maioria dos desenvolvedores evitará exceções e, em vez disso, criará outro método para indicar falha, devido ao suposto consumo de recursos.

Aqui está um exemplo de como evitar o uso de exceções em um cenário como o descrito acima, usando o padrão Try():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

Na minha opinião, a questão fundamental deveria ser se seria de se esperar que o chamador desejasse continuar o fluxo normal do programa se uma condição ocorresse.Se você não sabe, tenha métodos doSomething e trySomething separados, onde o primeiro retorna um erro e o segundo não, ou tenha uma rotina que aceite um parâmetro para indicar se uma exceção deve ser lançada se falhar).Considere uma classe para enviar comandos a um sistema remoto e relatar respostas.Certos comandos (por ex.reiniciar) fará com que o sistema remoto envie uma resposta, mas depois não responda por um determinado período de tempo.Portanto, é útil poder enviar um comando "ping" e descobrir se o sistema remoto responde em um período de tempo razoável sem ter que lançar uma exceção se isso não acontecer (o chamador provavelmente esperaria que os primeiros " ping" falhariam, mas uma delas eventualmente funcionaria).Por outro lado, se houver uma sequência de comandos como:

  exchange_command("open tempfile");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("close tempfile");
  exchange_command("copy tempfile to realfile");

seria desejável que a falha de qualquer operação abortasse toda a sequência.Embora seja possível verificar cada operação para garantir que ela foi bem-sucedida, é mais útil fazer com que a rotina exchange_command() lance uma exceção se um comando falhar.

Na verdade, no cenário acima pode ser útil ter um parâmetro para selecionar vários modos de tratamento de falhas:nunca lance exceções, lance exceções apenas para erros de comunicação ou lance exceções em qualquer caso em que um comando não retorne uma indicação de "sucesso".

para mim, a exceção deve ser lançada quando uma regra técnica ou de negócios necessária falha.por exemplo, se uma entidade automotiva estiver associada a uma matriz de 4 pneus ...se um pneu ou mais forem nulos...uma exceção deve ser disparada "NotEnoughTiresException" , porque pode ser capturada em diferentes níveis do sistema e ter um significado significativo por meio do registro.além disso, se tentarmos apenas controlar o fluxo do nulo e evitar a instanciação do carro.talvez nunca encontremos a origem do problema, porque o pneu não deveria ser nulo em primeiro lugar.

Você pode usar exceções um pouco genéricas para essas condições.Por exemploArgumentException deve ser usado quando algo dá errado com os parâmetros de um método (com exceção de ArgumentNullException).Geralmente você não precisaria de exceções como LessThanZeroException, NotPrimeNumberException etc.Pense no usuário do seu método.O número de condições que ela desejará tratar especificamente é igual ao número do tipo de exceções que seu método precisa lançar.Dessa forma, você pode determinar o quão detalhadas serão as exceções.

A propósito, sempre tente fornecer algumas maneiras para os usuários de suas bibliotecas evitarem exceções.TryParse é um bom exemplo, ele existe para que você não precise usar int.Parse e capturar uma exceção.No seu caso, você pode fornecer alguns métodos para verificar se o nome de usuário é válido ou a senha está correta, para que seus usuários (ou você) não precisem lidar com muitas exceções.Esperamos que isso resulte em código mais legível e melhor desempenho.

Em última análise, a decisão se resume a saber se é mais útil lidar com erros no nível do aplicativo como esse usando o tratamento de exceções ou por meio de seu próprio mecanismo caseiro, como o retorno de códigos de status.Não acho que exista uma regra rígida sobre o que é melhor, mas consideraria:

  • Quem está chamando seu código?Esta é algum tipo de API pública ou uma biblioteca interna?
  • Que linguagem você está usando?Se for Java, por exemplo, lançar uma exceção (marcada) impõe uma carga explícita ao chamador para lidar com essa condição de erro de alguma forma, em oposição a um status de retorno que pode ser ignorado.Isso pode ser bom ou ruim.
  • Como são tratadas outras condições de erro no mesmo aplicativo?Os chamadores não vão querer lidar com um módulo que lida com erros de maneira idiossincrática, diferente de qualquer outra coisa no sistema.
  • Quantas coisas podem dar errado na rotina em questão e como elas seriam tratadas de maneira diferente?Considere a diferença entre uma série de blocos catch que tratam de erros diferentes e uma ativação de um código de erro.
  • Você tem informações estruturadas sobre o erro que precisa retornar?Lançar uma exceção oferece um lugar melhor para colocar essas informações do que apenas retornar um status.

Existem duas classes principais de exceção:

1) Exceção do sistema (por exemplo, conexão com o banco de dados perdida) ou 2) Exceção do usuário.(por exemplo, validação de entrada do usuário, 'senha incorreta')

Achei útil criar minha própria classe de exceção de usuário e quando quero lançar um erro de usuário, quero ser tratado de maneira diferente (ou seja, erro de recursos exibido para o usuário), então tudo que preciso fazer em meu manipulador de erros principal é verificar o tipo de objeto :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

Algumas coisas úteis para pensar ao decidir se uma exceção é apropriada:

  1. qual nível de código você deseja executar após a ocorrência do candidato à exceção - ou seja, quantas camadas da pilha de chamadas devem ser desenroladas.Geralmente, você deseja tratar uma exceção o mais próximo possível de onde ela ocorre.Para validação de nome de usuário/senha, você normalmente lidaria com falhas no mesmo bloco de código, em vez de deixar surgir uma exceção.Portanto, uma exceção provavelmente não é apropriada.(OTOH, após três tentativas de login malsucedidas, o fluxo de controle pode mudar para outro lugar e uma exceção pode ser apropriada aqui.)

  2. Este evento é algo que você gostaria de ver em um log de erros?Nem toda exceção é gravada em um log de erros, mas é útil perguntar se essa entrada em um log de erros seria útil - ou seja, você tentaria fazer algo a respeito ou seria o lixo que você ignora.

"PasswordNotCorrectException" não é um bom exemplo de uso de exceções.É de se esperar que usuários erram suas senhas, então não é uma exceção, IMHO.Você provavelmente até se recupera disso, mostrando uma bela mensagem de erro, então é apenas uma verificação de validade.

Exceções não tratadas eventualmente interromperão a execução - o que é bom.Se estiver retornando códigos falsos, nulos ou de erro, você terá que lidar sozinho com o estado do programa.Se você esquecer de verificar as condições em algum lugar, seu programa poderá continuar rodando com dados errados e você poderá ter dificuldade em descobrir o que aconteceu e onde.

É claro que você poderia causar o mesmo problema com instruções catch vazias, mas pelo menos identificá-las é mais fácil e não exige que você entenda a lógica.

Então, como regra geral:

Use-os onde você não quiser ou simplesmente não conseguir se recuperar de um erro.

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