Tratamento de Erros e Excepções
Published: 2024-05-29 15:25:27


## Definições relacionadas
**- Erro:** É uma falha no código que impede a execução correcta de um programa. Erros podem ser de sintaxe, de lógica ou de execução. Um erro é uma condição inesperada que interrompe o fluxo normal do programa.
**- Excepção:** É um evento anormal ou inesperado que ocorre durante a execução de um programa e interrompe o fluxo normal das instruções. É uma situação anormal que pode ser prevista e tratada dentro do programa.
**- Efeitos colaterais:** São mudanças no estado do sistema que ocorrem como resultado da execução de uma função ou procedimento, que não estão relacionadas directamente ao seu retorno esperado. Ex: uma função que modifica uma variável global ou altera o conteúdo de um arquivo.
**- Tratamento de Erros / Excepções:** São práticas e mecanismos implementados para lidar com erros e excepções de forma controlada e previsível. Isso pode incluir o uso de blocos _**try-catch-finally**_, logging de erros, notificações ao usuário e outras acções para mitigar o impacto de problemas e garantir a robustez do sistema.
**- Programação defensiva:** É a abordagem de codificação que visa antecipar e lidar com possíveis erros ou condições inesperadas para garantir que o software continue funcionando corretamente, mesmo em casos não previstos.

<br><br>

## Similaridades e Diferenças 
### Similaridades
- Ambos indicam problemas ou eventos anormais durante a execução do programa.
- Ambos podem interromper o fluxo normal do programa.
- Ambos exigem tratamento adequado para evitar falhas no programa.

### Diferenças
**- Origem:** Os erros geralmente se originam de problemas no código do programa, enquanto as excepções geralmente se originam de eventos externos ao programa.
**- Previsibilidade:** Erros geralmente são imprevisíveis, enquanto excepções podem ser previstas e antecipadas.
**- Tratamento:** O tratamento de erros nem sempre é possível, enquanto as excepções devem sempre ser tratadas ou propagadas.
**- Natureza:** Erros de execução podem incluir problemas como estouro de memória, enquanto excepções incluem condições como arquivos não encontrados ou acessos fora dos limites de um array.

<br><br>

## Cenários mais comuns de Erros e Excepções
**- Manipulação de Arquivos:** Durante a leitura ou escrita de arquivos, podem ocorrer erros como "Arquivo não encontrado", "Permissão negada" ou "Erro de E/S". É importante verificar a existência de arquivos antes de abri-los e tratar as excepções relacionadas à abertura de arquivos.
**- Manipulação de Base de dados:** Durante a manipulação das bases de dados podem ocorrer erros como “Erro de conexão”, "Tabela não encontrada", "Chave duplicada" ou "Violação de chave estrangeira" . É importante verificar a integridade dos dados antes de inseri-los no banco de dados e tratar as excepções relacionadas às operações de banco de dados.
**- Chamadas HTTP:** Durante as chamadas HTTP podem ocorrer erros como "Conexão recusada", "Erro de tempo limite" ou "Erro de servidor". É importante verificar a conexão antes de fazer uma chamada HTTP e tratar as excepções relacionadas às chamadas HTTP.
**- Retorno de Respostas HTTP:** Durante o processamento das respostas HTTP  podem ocorrer erros como "Código de status inválido" ou "Erro de sintaxe no corpo da resposta". É importante verificar o código de status antes de processar o corpo da resposta e tratar as excepções relacionadas ao processamento das respostas HTTP.
**- Conexões TCP, UDP:** Durante as conexões TCP e UDP podem ocorrer erros como "Conexão recusada", "Erro de tempo limite" ou "Erro de rede". É importante verificar a conexão antes de enviar ou receber dados e tratar as excepções relacionadas às conexões TCP e UDP.
**- Conexão com servidores remotos:** Durante as conexões com servidores remotos podem ocorrer erros como "Conexão recusada", "Erro de tempo limite" ou "Erro de DNS". É importante verificar a conexão antes de enviar ou receber dados e tratar as excepções relacionadas às conexões com servidores remotos.
**- Concorrência e Paralelismo:** Durante a execução concorrente ou paralela de código  podem ocorrer erros como "Deadlock", "Race condition" ou "Erro de memória". É importante usar técnicas de programação defensiva, como bloqueios ou semáforos, e tratar as excepções relacionadas à concorrência e paralelismo.

<br><br>

## Abordagem no Paradigma Orientadas a Objectos
Nas linguagens de programação orientadas a objectos, o tratamento de excepções é geralmente feito usando blocos _**try, catch, throw e finally**_. Esses blocos fazem parte das boas práticas de programação orientada a objectos e permitem que os programadores tratem excepções de maneira estruturada e organizada.
No bloco _**try**_, é possível definir o código principal que será executado. Se houver algum erro ou excepção durante a execução desse código, o controle será passado para o bloco catch, onde é possível tratar a excepção de maneira adequada.
O comando _**throw**_ é usado para lançar uma excepção manualmente dentro do código. Isso pode ser útil quando é necessário interromper a execução do código em uma determinada situação e retornar um erro específico.
O bloco _**finally**_ pode ser usado em conjunto com try-catch para garantir que determinadas instruções sejam executadas independentemente de ocorrer uma excepção ou não. Isso pode ser útil para garantir a liberação de recursos ou o fechamento de conexões, por exemplo.
Embora haja programadores que discordam do padrão try-catch-finally, é importante admitir que funciona e deve ser aplicados para o tratamento de blocos de execução. O uso adequado de _**try-catch-finally**_ pode ajudar a evitar erros inesperados e melhorar a robustez e a confiabilidade do código.

<br><br>

## Abordagem no Paradigma funcional
No paradigma funcional, o tratamento de erros se diferencia da abordagem tradicional orientada a objectos, oferecendo mecanismos mais robustos e expressivos para lidar com falhas e situações inesperadas. Ao invés de utilizar blocos try-catch para capturar excepções, as linguagens funcionais Adoptam técnicas que previnem e propagam erros de forma mais natural e segura.

### Principais Características
**- Imutabilidade:** Valores imutáveis garantem que os dados não sejam alterados após sua criação, evitando efeitos colaterais e facilitando o raciocínio sobre o estado do programa. Isso torna o código mais previsível e menos propenso a erros.
**- Gerenciamento de Retorno:** Funções puras retornam um valor ou um tipo especial que indica a ausência de um valor. Essa abordagem elimina a necessidade de lançar excepções para indicar falhas, tornando o código mais declarativo e menos propenso a erros não tratados.
**- Composição Funcional:** Funções podem ser compostas para criar funções mais complexas, sem a necessidade de propagar explicitamente erros entre elas. Isso promove a modularidade e reutilização de código, facilitando o tratamento de erros de forma consistente.
**- Tipagem Estática:** A tipagem forte e inferida garante que os valores utilizados nas funções sejam compatíveis com os tipos esperados, evitando erros de tipo em tempo de execução. Isso torna o código mais robusto e menos propenso a falhas inesperadas.

### Vantagens
**- Código mais robusto e confiável:** A imutabilidade, o gerenciamento de retorno e a tipagem forte garantem que o código seja menos propenso a erros e falhas inesperadas.
**- Melhora na legibilidade e expressividade:** O código funcional é geralmente mais conciso e declarativo, facilitando a leitura e o entendimento da lógica do programa.
**- Facilidade de raciocínio sobre o estado do programa:** A imutabilidade torna o estado do programa mais previsível e facilita o raciocínio sobre as possíveis falhas.
**- Maior segurança e confiabilidade:** A ausência de efeitos colaterais e a tipagem forte garantem que o código seja mais seguro e confiável.

### Desafios
**- Mudança de paradigma:** Aprender a programar no paradigma funcional pode ser um desafio para programadores acostumados à programação orientada a objectos.
**- Falta de familiaridade com ferramentas e bibliotecas:** As ferramentas e bibliotecas para desenvolvimento funcional podem ser menos conhecidas e acessíveis do que as ferramentas para linguagens orientadas a objectos.
**- Curva de aprendizado mais íngreme:** A curva de aprendizado para dominar o paradigma funcional pode ser mais íngreme do que para linguagens orientadas a objectos.

### Exemplos de Abordagens Diferentes
**- Haskell:** Utiliza os tipos Maybe com _**Just**_ e _**Nothing**_ para indicar a presença ou ausência de um valor, permitindo lidar com falhas de forma declarativa. Para erros mais detalhados, usa o tipo Either com Left para erros e Right para valores corretos.
**- Erlang:** Adopta o conceito de "processos leves" para isolar falhas e garantir a confiabilidade do sistema. Cada processo é independente, e falhas são tratadas através de um modelo de supervisão onde processos supervisores reiniciam processos falhos.
**- Scala:** Combina recursos da programação funcional e orientada a objetos, oferecendo mecanismos flexíveis para tratamento de erros, como _**Option, Try, e Either**_, que permitem uma abordagem robusta e composicional para lidar com falhas.
**- Rust:** Utiliza os tipos _**Result e Option**_ para tratamento de erros. Result é usado para operações que podem falhar, com os variantes _**Ok**_ para sucesso e _**Err**_ para erro. Rust também promove a propagação de erros com o operador ?, permitindo um código mais conciso e seguro para erros de execução.
**- F#:** Como uma linguagem funcional da família .NET, F# oferece recursos poderosos para tratamento de erros. Ele utiliza discriminação de união, como _**Option**_ e _**Result**_, e expressões match para lidar com valores de retorno opcionais e tratamento de erros de forma elegante e segura.

Apesar dos desafios, o paradigma funcional vem ganhando cada vez mais popularidade, e as técnicas de tratamento de erros funcionais estão sendo cada vez mais Adoptadas em linguagens tradicionais como C#, Java, Kotlin e Swift.
É importante ressaltar que a escolha da melhor abordagem para tratamento de erros depende do contexto específico do projeto, das características da linguagem de programação utilizada e da preferência pessoal do programador.

<br><br>

## Destancando a Linguagem Go
A linguagem Go se destaca por sua abordagem singular ao tratamento de erros, optando por não utilizar o paradigma tradicional de excepções try-catch-throw-finally. Essa escolha, inicialmente desafiadora para alguns programadores, oferece vantagens significativas em termos de simplicidade, confiabilidade e controle sobre o fluxo do programa.

### Fundamentos do Tratamento de Erros em Go
**- Erro como Tipo de dado:** Em Go, o erro é representado pelo tipo _**error**_. Isso significa que as funções podem retornar um valor de erro, permitindo que o programador identifique e trate falhas explícitamente.
**- Verificação de Erros:** A verificação de erros é essencial em Go. Após chamar uma função, é crucial verificar se o valor de retorno é um erro. Caso positivo, é possível registrar o erro, notificar o usuário ou interromper a execução do programa.
**- Propagação de Erros:** Os erros podem ser propagados através de várias chamadas de função. Ao invés de lançar excepções, Go utiliza a técnica de propagação de erros, permitindo que o erro seja tratado em um nível superior do código, caso necessário.
**- Retorno de Erros em Funções sem Retorno:** Em Go, é comum que funções que não possuem um valor de retorno explícito retornem um valor de erro. Isso garante que as falhas sejam comunicadas de forma consistente e que os programadores estejam cientes das possíveis situações de erro.

### Vantagens da Abordagem de Go
**- Simplicidade:** O modelo de tratamento de erros em Go é mais simples e directo, evitando a complexidade dos blocos try-catch e excepções. Isso torna o código mais fácil de ler, entender e depurar.
**- Confiabilidade:** Ao tratar erros explicitamente, Go incentiva a escrita de código mais robusto e confiável. Os programadores assumem a responsabilidade de lidar com falhas, garantindo que o programa se comporte de forma previsível em diferentes cenários.
**- Controle Granular:** Os programadores podem decidir como lidar com cada tipo de erro, personalizando o comportamento do programa de acordo com suas necessidades.
**- Desempenho:** A abordagem de Go pode ser mais performante em alguns casos, pois evita a sobrecarga de excepções e o uso de mecanismos de captura e propagação de erros.

### Desafios da Abordagem de Go
**- Mudança de Mentalidade:** Para programadores acostumados com linguagens que utilizam excepções, a abordagem de Go pode exigir uma mudança de mentalidade e adaptação.
**- Gerenciamento Manual de Erros:** O tratamento manual de erros em todas as funções pode se tornar trabalhoso em projectos maiores, exigindo disciplina e atenção aos detalhes.
**- Falta de Mecanismos Automáticos:** Go não possui mecanismos automáticos para lidar com erros não tratados, como um manipulador de excepções global. Isso requer que o programador implemente suas próprias estratégias para lidar com essas situações.

<br><br>

## Recomendações
Para criar software robusto e resiliente, é fundamental tratar os erros e excepções de forma eficaz:
- Considerar possíveis pontos de falha em todo o código.
- Usar programação defensiva para antecipar problemas.
- Fornecer mensagens de erro claras e documentar as excepções.
- Libertar recursos adequadamente para evitar leaks e outros problemas.

Seguindo essas recomendações e aplicando as práticas exemplificadas, é possível construir sistemas que lidem com situações inesperadas sem comprometer a integridade e a experiência do usuário.

<br><br>

## Conclusão
Devemos pensar que um bloco de instruções pode falhar. Um programa sempre lida com problemas inesperados, bem como efeitos colaterais. Para esses casos é recomendado tratar os erros e excepções.
Sistemas computacionais estão sempre sujeitos a falhas, por isso é crucial que o programador analise e identifique possíveis cenários onde erros podem ocorrer. Antecipar esses problemas e implementar estratégias de tratamento de erros robustas é essencial para garantir a resiliência e a confiabilidade do software.

<br><br>

## Exemplo
Implementação de Upload de Arquivos em Go e C#, tratando os cenários de erros.
Repositórios:
- https://github.com/ortizdavid/go-nopain/filemanager
- https://github.com/ortizdavid/AspnetCoreRestApi



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0j40cls5ejgyumwnws2o.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zl15u9tra1sbb9eqatt.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhz75wjs0zacn2k9be5g.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emu9osrw4ws08tun5yal.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snsu0rjmhcvxi0bsosc7.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/128hkhjaewmevs4iq9pk.png)



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebigdichqs2eslhm3lpr.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qky83g4lmje3watkbjrj.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqsg7l6qbapetg0cd8q4.png)