Pendant que je lis je lis le programmeur pragmatique E2 , je suis tombé sur Astuce 38: Crash tôt . Fondamentalement, l'auteur, du moins à ma compréhension, vous conseille d'éviter d'attraper des exceptions et de laisser tomber le programme. Il continue à dire:
L'un des avantages de la détection des problèmes dès que vous pouvez, c'est que vous pouvez vous écraser plus tôt et que vous avez fait une crise, c'est souvent la chose de pari que vous pouvez faire. L'alternative peut être de continuer, d'écrire des données corrompues dans une base de données vitale ou de commander la machine à laver dans son vingtième cycle de spin consécutif.
Plus tard, il dit:
Dans ces environnements, des programmes sont conçus pour échouer, mais que l'échec est géré avec des superviseurs. Un superviseur est responsable du code d'exécution et sait quoi faire au cas où le code échoue, ce qui pourrait inclure le nettoyage après cela, le redémarrer, etc.
Je me lance de refléter cela en vrai code. Qu'est-ce qui pourrait être le superviseur que l'auteur se réfère? En Java, je suis utilisé pour utiliser beaucoup d'essais/attraper. Dois-je arrêter de faire ça? Et remplacer cela avec quoi? Dois-je simplement laisser le programme redémarrer chaque fois qu'il y a une exception?
Voici l'exemple utilisé par l'auteur ( Elixir ):
try do
add_score_to_board(score);
rescue
InvalidScore
Logger.error("Can't add invalid score. Exiting");
raise
rescue
BoardServerDown
Logger.error("Can't add score: Board is down. Existing");
raise
rescue
StaleTransaction
Logger.error("Can't add score: stale transaction. Existing");
raise
end
C'est ainsi que les programmeurs pragmatiques écriraient ceci:
add_score_to_board(score);
En principe, votre code devrait gérer des situations inhabituelles, mais elle ne devrait pas gérer les erreurs de programmation (et ne pas s'attendre à ce qu'une situation inhabituelle puisse arriver est une erreur de programmation).
S'il y a des erreurs de programmation, votre code devrait s'écraser, puis un développeur déterminer pourquoi il s'est écrasé et le fixe. S'il y a des situations inhabituelles, votre code doit les gérer si cela est possible et sûr.
Qu'est-ce qui pourrait être le superviseur que l'auteur se réfère?
Cela dépend du type de programme. Dans le cas très fréquent d'un serveur Web, le " superviseur " serait un fil répartiteur que les mains hors des demandes individuelles de threads de travail à traiter. S'il y a une exception alors qu'une demande est traitée, il est tout à fait acceptable (et en commun de fait) la pratique de simplement laisser cette bulle d'exception à certains gestionnaire d'exceptions de haut niveau, qui enregistre l'exception, annule toutes les transactions DB et envoie un HTTP 500 réponse d'erreur au client. Le thread de travail peut alors commencer à traiter la demande suivante. Pour un programme GUI autonome, vous pouvez imaginer quelque chose de similaire dans le gestionnaire d'événement qui répond à l'entrée d'utilisateur, bien qu'ici il y a un danger de laisser l'interface graphique dans un état corrompu.
En Java, je suis habitué à utiliser beaucoup de try/catch, dois-je arrêter de faire ça?
Ça dépend. Dans de nombreux cas, des exceptions spécifiques indiquent les conditions d'erreur connues que votre programme peut et doit gérer. Par exemple, en essayant d'analyser une date un utilisateur est entré, mais ils sont entrés le 30 Février. Continue de faire ça.
Ce que vous devez non faire est catch(Exception e)
et puis juste continuer comme si rien ne se passait.
Java est unique en ce sens qu'il a vérifié exceptions que forces vous attraper (ou votre signature pollue la méthode avec). Ceci est largement considéré comme une expérience de la conception du langage échoué. Une façon assez commune pour faire face aux exceptions vérifiées vous ne pouvez pas utilement la poignée est de les envelopper dans un RuntimeException
, à savoir faire quelque chose comme catch(IOException e) { throw new RuntimeException("Error processing " + filename, e); }
- notez que vous pouvez encore faire quelque chose très utile par l'ajout d'informations (dans ce cas sur le fichier qui a été en cours d'accès) qui vous aideront dans le débogage lorsque l'exception est connecté.
et le remplacer par quoi? Est-ce que je laisse simplement le programme redémarre chaque fois qu'il ya exception?
Cela dépend du programme. Si vous pouvez trouver un centre, lieu de haut niveau où il est logique d'exceptions prises parce que vous pouvez les connecter et que le programme dans un état cohérent comme si toute action en fin de compte a provoqué l'exception n'a jamais été commencé, faire.
S'il n'y a pas lieu, oui, laissez le plantage du programme. Pour les utilitaires de ligne de commande, qui est souvent le bon choix.
Suite à la réponse de @ DocBrown, il convient également de lancer des erreurs/exceptions dans les clauses de la Garde précoce. La structure de l'exemple facilite déjà cela, mais les gens écrivent souvent du code qui vérifie les choses trop tard. Cela peut conduire à un code long, humide et très imbriqué, plus d'exécution que nécessaire, la logique booléenne complexe, plus de bugs et un résultat difficile à voir en un coup d'œil est sans bug. Ces préoccupations s'appliquent même si cela ne peut pas tomber. Mais si cela peut, et vous utilisez des clauses anticipées, vous n'évitez pas simplement les problèmes ci-dessus; Vous pouvez également supposer que certaines choses réussissent dans la plupart du code.
Qu'est-ce qui pourrait être le superviseur que l'auteur se réfère?
Cela pourrait être de nombreuses choses différentes, en fonction de la plate-forme et de l'environnement dans lequel le programme est en cours d'exécution.
Une possibilité est Daemontools . Il s'agit d'un programme basé sur UNIX qui prend en charge des programmes de démarrage et d'arrêt sous son contrôle, y compris les redémarrer si elles s'arrêtent de manière inattendue. J'en ai utilisé avec succès dans des environnements de production.
(Je pense que cela a été largement remplacé par Systemd , qui fait certaines des mêmes choses.)
Notez que cela ne peut le faire que si tout le programme s'arrête - si un fil se bloque, mais d'autres continuent, il n'a aucun moyen de le dire.
(Et, comme mentionné dans d'autres réponses, cela laisserait votre programme dans un État incohérent. Par exemple, envisagez un programme simple avec une lecture de fil d'un fil de données, et un autre l'écrivant à une DB. Si le fil du lecteur s'est écrasé, le L'écrivain pourrait heureusement continuer indéfiniment, sans aucune donnée à écrire. Toutefois, si l'écrivain fil s'est écrasé, le lecteur ne pouvait continuer que jusqu'à ce que la mémoire soit pleine de données en attente d'être écrites - ce qui pourrait prendre beaucoup de temps. De toute façon, il se manquera. De nombreuses minutes ou des heures de données de l'aliment. ATTENDU QUE si l'ensemble du programme arrête, il manquerait des données pour seulement les quelques secondes qu'il a fallu pour noter le problème, arrêté et être redémarré par le superviseur.)
Donc, si votre programme utilise plusieurs threads (directement ou non), il n'est pas sécuritaire de laisser une exception à découvrir; Pour la sécurité, vous devrez peut-être configurer un gestionnaire d'exception global non capturé qui fait une fermeture explicite de l'ensemble du programme. (Un tel gestionnaire doit être très soigneusement écrit, dans le cas , il jette une exception ... des conditions hors mémoire sont particulièrement difficiles à gérer de manière fiable.)
In Java Il y a presque deux décennies, le concept a été appelé "échoue rapide" non "crash tôt". Le problème principal est que le problème principal La plupart du temps, vous avez eu un programme de serveur qui manipule plusieurs demandes et il ne pouvait pas s'arrêter pour un problème soulevé en une seule demande, les gens ont bientôt découvert que lorsqu'un programme écrit juste quelques lignes de journalisation lorsqu'une exception est levée la majeure partie des temps. être submergé par une énorme quantité de rapports triviaux et de dépannage se transforme en une creuse douloureuse dans des piles de fichiers journaux. L'approche la plus pragmatique consistait à envoyer des rapports d'erreur aux applications externes et, en même temps, des assertions utilisées qui sont des exceptions désactivées par défaut À moins que le processus ne soit démarré avec le drapeau Activer les assertions. Le but des affirmations était d'arrêter de traiter rapidement dans des environnements de test laissant les gens à repérer immédiatement les problèmes possibles, d'où le terme échoue rapidement, par opposition à "comportement résilient".
Malheureusement, plus tard sur Junit réutilisé le mot clé Assert et créé beaucoup de confusion à ce sujet, mais c'est un autre problème.
Avec le développement d'Enterprise Java et des programmes de serveur reprochent plusieurs instances pour traiter les demandes du client La nécessité de continuer à fonctionner lorsqu'une exception est réduite et que les gens ont commencé à dire que si vous ne savez pas quoi faire Dans un bloc de capture, vous feriez mieux de ne pas envelopper le code avec un essai/attrape qui ne cacherait pas l'exception à l'appelant, mais cela ne signifie pas que vous ne devriez pas utiliser Essayer/Catch du tout, des exceptions doivent toujours être correctes que possible. Vous ne pouvez les ignorer que si vous êtes sûr que d'une manière ou d'une autre, elles seront gérées à des niveaux plus élevés dans la pile d'appels.
Dernièrement, l'idée de frayer un enfant pour chaque demande a été élargie par les défenseurs de la programmation réactive dans le même temps d'obtenir un comportement rapide et résilient en même temps. À condition que vous disposiez d'un cadre capable de superviser, surveiller, manipuler automatiquement les demandes échouées. D'où l'idée du superviseur tirée du modèle acteur . Ou mieux disons que le superviseur est un rôle du modèle de l'acteur tel qu'il a été décrit dans la programmation fonctionnelle, voir pour un exemple le AKKA Framework ou le modèle de l'acteur dans scala