1. Le métier de programmeur

Très souvent, les programmeurs novices voient le travail d'un programmeur complètement différemment de ce que pensent les programmeurs expérimentés .

Les débutants disent souvent quelque chose comme "Le programme fonctionne, de quoi d'autre avez-vous besoin ?" Un programmeur expérimenté sait que "fonctionner correctement" n'est qu'une des conditions requises pour un programme , et ce n'est même pas la chose la plus importante !

Lisibilité du code

La chose la plus importante est que le code du programme soit compréhensible pour les autres programmeurs . Ceci est plus important qu'un programme qui fonctionne correctement. Beaucoup plus.

Si vous avez un programme qui ne fonctionne pas correctement, vous pouvez le réparer. Mais si vous avez un programme dont le code est incompréhensible, vous ne pouvez rien en faire.

Prenez simplement n'importe quel programme compilé, tel que le bloc-notes, et changez sa couleur d'arrière-plan en rouge. Vous avez un programme fonctionnel, mais vous n'avez pas de code source compréhensible : il est impossible d'apporter des modifications à un programme comme celui-là.

Un exemple classique est lorsque les développeurs de Microsoft ont supprimé le jeu Pinball de Windows car ils ne pouvaient pas le porter sur une architecture 64 bits. Et ils avaient même son code source. Ils ne pouvaient tout simplement pas comprendre comment le code fonctionnait .

Prise en compte de chaque cas d'utilisation

La deuxième exigence la plus importante pour un programme est de tenir compte de chaque scénario. Souvent, les choses sont un peu plus compliquées qu'il n'y paraît.

Comment un programmeur novice voit l'envoi de messages SMS :

Un programme qui fonctionne correctement

Comment un programmeur professionnel le voit :

Un programme qui fonctionne correctement

Le scénario "fonctionne correctement" n'est généralement qu'un des nombreux possibles. Et c'est pourquoi de nombreux débutants se plaignent du validateur de tâches de CodeGym : un seul scénario sur 10 fonctionne, et le programmeur débutant pense que cela suffit.


2. Situations anormales

Situations anormales

Des situations anormales peuvent survenir lors de l'exécution de n'importe quel programme.

Par exemple, vous décidez d'enregistrer un fichier mais il n'y a pas d'espace disque. Ou le programme essaie d'écrire des données dans la mémoire, mais la mémoire disponible est faible. Ou vous téléchargez une image à partir d'Internet, mais la connexion est perdue pendant le processus de téléchargement.

Pour chaque situation anormale, le programmeur (l'auteur du programme) doit a) l' anticiper , b) décider exactement comment le programme doit la gérer , et c) écrire une solution aussi proche que possible de celle souhaitée.

C'est pourquoi les programmes ont longtemps eu un comportement très simple : si une erreur se produisait dans le programme, le programme se terminait. Et c'était une très bonne approche.

Supposons que vous souhaitiez enregistrer un document sur le disque, au cours du processus d'enregistrement, vous découvrez qu'il n'y a pas assez d'espace disque. Quel comportement aimeriez-vous le plus :

  • Le programme se termine
  • Le programme continue de s'exécuter, mais n'enregistre pas le fichier.

Un programmeur novice peut penser que la deuxième option est meilleure, car le programme est toujours en cours d'exécution. Mais en réalité ce n'est pas le cas.

Imaginez que vous ayez tapé un document dans Word pendant 3 heures, mais deux minutes après le début de votre processus d'écriture, il est devenu évident que le programme ne serait pas en mesure d'enregistrer le document sur le disque. Vaut-il mieux perdre deux minutes de travail ou trois heures ?

Si le programme ne peut pas faire ce qu'il doit faire, il est préférable de le laisser fermer plutôt que de continuer à prétendre que tout va bien. La meilleure chose qu'un programme puisse faire lorsqu'il rencontre une panne qu'il ne peut pas résoudre par lui-même est de signaler immédiatement le problème à l'utilisateur.


3. Contexte des exceptions

Les programmes ne sont pas les seuls à faire face à des situations anormales. Ils se produisent également à l'intérieur des programmes - dans les méthodes. Par exemple:

  • Une méthode veut écrire un fichier sur le disque, mais il n'y a pas d'espace.
  • Une méthode veut appeler une fonction sur une variable, mais la variable est égale à null.
  • La division par 0 se produit dans une méthode.

Dans ce cas, la méthode appelante pourrait éventuellement corriger la situation (exécuter un scénario alternatif) si elle sait quel type de problème s'est produit dans la méthode appelée.

Si nous essayons de sauvegarder un fichier sur le disque et qu'un tel fichier existe déjà, nous pouvons simplement demander à l'utilisateur de confirmer que nous devons écraser le fichier. S'il n'y a pas d'espace disque disponible, nous pouvons afficher un message à l'utilisateur et lui demander de sélectionner un autre disque. Mais si le programme manque de mémoire, il plantera.

Il était une fois des programmeurs qui se sont penchés sur cette question et ont trouvé la solution suivante : toutes les méthodes/fonctions doivent retourner un code d'erreur qui indique le résultat de leur exécution. Si une fonction fonctionnait parfaitement, elle retournait 0 . Si ce n'est pas le cas, il renvoie un code d'erreur (pas zéro).

Avec cette approche des erreurs, après presque chaque appel de fonction, les programmeurs devaient ajouter une vérification pour voir si la fonction se terminait par une erreur. Le code a augmenté de taille et a ressemblé à ceci :

Code sans gestion des erreurs Code avec gestion des erreurs
File file = new File("ca:\\note.txt");
file.writeLine("Text");
file.close();
File file = new File("ca:\\note.txt");
int status = file.writeLine("Text");
if (status == 1)
{
   ...
}
else if (status == 2)
{
   ...
}
status = file.close();
if (status == 3)
{
   ...
}

De plus, bien souvent une fonction qui découvrait qu'une erreur s'était produite ne savait pas quoi en faire : l'appelant devait renvoyer l'erreur, et l'appelant de l'appelant la renvoyait à son appelant, et ainsi de suite.

Dans un gros programme, une chaîne de dizaines d'appels de fonction est la norme : parfois vous pouvez même trouver une profondeur d'appel de centaines de fonctions. Et maintenant, vous devez transmettre le code d'erreur du bas vers le haut. Et si quelque part en cours de route, une fonction ne gère pas le code de sortie, l'erreur sera perdue.

Un autre inconvénient de cette approche est que si les fonctions renvoyaient un code d'erreur, elles ne pouvaient plus renvoyer les résultats de leur propre travail. Le résultat des calculs devait être transmis via des paramètres de référence. Cela rendait le code encore plus lourd et augmentait encore le nombre d'erreurs.