Qu'est-ce qui définit un code robuste?

42

Mon professeur ne cesse de se référer à cet exemple Java lorsqu'il parle de code "robuste":

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Il affirme que "code robuste" signifie que votre programme prend en compte toutes les possibilités et qu'il n'existe pas d'erreur - toutes les situations sont gérées par le code et résultent en un état valide, d'où le "else".

Je suis douteux, cependant. Si la variable est un booléen, à quoi sert-il de vérifier un troisième état lorsqu'un troisième état est logiquement impossible?

"Ne pas avoir d'erreur" semble également ridicule; même les applications Google montrent les erreurs directement à l'utilisateur au lieu de les avaler en silence ou de les considérer comme un état valide. Et c'est bien - j'aime savoir quand quelque chose ne va pas. Et il semble tout à fait la prétention de dire qu'une application n'aurait jamais d'erreur.

Alors, quelle est la définition réelle de "code robuste"?

Lotus Notes
la source
4
Cela ne serait valable que dans un langage peu typé. Dans un langage fortement typé, une variable de type booléen (et non un entier se présentant comme un booléen), ne peut être que vrai ou faux, il n'y a pas de troisième option ...
Marjan Venema
23
demandez-lui comment vérifieriez-vous la couverture du 3ème cas, car un code robuste devrait obligatoirement être testé, et si vous n'arriviez pas à tester le 3ème cas, vous ne pourriez pas détecter les bogues qui pourraient s'y cacher.
gbjbaanb
2
@Marjan - dans un langage pas très typé, on pourrait très bien écrire: if (var) {} else {}
kevin cline
2
Je ne connais aucune langue où x et! X pourraient être vrais. Notez que je n'ai pas suggéré "if (x == true) ..."; Je déteste de telles comparaisons.
Kevin Cline

Réponses:

33

à quoi sert de vérifier un troisième état lorsqu'un troisième est logiquement impossible?

Qu'en est-il d'un Boolean?qui permet un NULLétat qui n'est ni vrai ni faux. Maintenant que doit faire le logiciel? Certains logiciels doivent être très résistants aux chocs, comme les stimulateurs cardiaques. Vous avez déjà vu quelqu'un ajouter une colonne à une base de données Booleanet initialiser les données actuelles NULL? Je sais que je l'ai vu.

Voici quelques liens qui expliquent ce que signifie être robuste en termes de logiciel:

Si vous pensez qu'il existe une définition universellement acceptée de «robuste» ici, bonne chance. Il peut y avoir des synonymes comme anti-bombes ou anti-imbéciles. Le programmateur de bandes magnétiques est un exemple de quelqu'un qui écrit habituellement du code robuste, du moins dans ma compréhension des termes.

JB King
la source
13
S'il s'agissait d'un booléen nullable, Java et c # liraient de manière à ce que NULL soit vérifié en premier.
Esben Skov Pedersen
Il ne semble pas exister de définition universellement convenue de ce que sont un chat ou un chien.
Tulains Córdova
11

Pour les besoins de ma discussion, un Bool peut avoir 2 états, True ou False. Tout le reste n'est pas conforme à la spécification de programmation Langugae. Si votre chaîne d’outils n’est pas conforme à ses spécifications, vous êtes blessé, peu importe ce que vous faites. Si un développeur crée un type de Bool comportant plus de 2 états, c'est la dernière chose qu'il ferait dans ma base de code.

Option A.

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

Option B

if (var == true) {
    ...
} else {
    ...
}

J'affirme que l'option B est plus robuste .....

Tout twit peut vous dire de gérer les erreurs inattendues. Ils sont généralement faciles à détecter une fois que vous y pensez. L'exemple que votre professeur a donné n'est pas quelque chose qui pourrait arriver, c'est donc un très mauvais exemple.

A est impossible à tester sans harnais de test compliqués. Si vous ne pouvez pas le créer, comment allez-vous le tester? Si vous n'avez pas testé le code, comment savez-vous que cela fonctionne? Si vous ne savez pas que cela fonctionne, vous n’écrivez pas un logiciel robuste. Je pense qu'ils appellent encore cela un Catch22 (excellent film, regardez-le un jour).

L'option B est simple à tester.

Problème suivant, posez cette question à votre professeur: "Que voulez-vous que je fasse à ce sujet si un booléen n’est ni vrai ni faux?" Cela devrait mener à une discussion très intéressante .....

Dans la plupart des cas, un dépotoir est approprié, au pire, il gêne l'utilisateur ou coûte très cher. Que faire si, par exemple, le module est le système de calcul de la rentrée en temps réel de la navette spatiale? Toute réponse, aussi inexacte soit-elle, ne peut être pire que d’annuler, ce qui tuerait les utilisateurs. Alors que faire, si vous savez que la réponse peut être fausse, optez pour le 50/50, ou abandonnez et tentez l'échec à 100%. Si j'étais un membre de l'équipage, je prendrais le 50/50.

L'option A me tue L'option B me donne une chance égale de survie.

Mais attendez - c'est une simulation de la rentrée dans la navette spatiale - alors quoi? Abort pour que tu saches. Cela vous semble une bonne idée? - NON - parce que vous devez tester avec le code que vous prévoyez d'expédier.

L'option A est préférable pour la simulation, mais ne peut pas être déployée. C'est inutile L'option B étant le code déployé, la simulation fonctionne de la même manière que les systèmes en direct.

Disons que c'était une préoccupation valable. La meilleure solution serait d'isoler le traitement des erreurs de la logique d'application.

if (var != true || var != false) {
    errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
 .... 
} else {
 .... 
}

Lecture ultérieure - machine à rayons X Therac-25, échec de la fusée Ariane 5 et autres (Link a beaucoup de liens cassés mais suffisamment d’informations pour que Google puisse vous aider)

Mattnz
la source
1
"..des erreurs inattendues. Elles sont généralement faciles à détecter une fois que vous y pensez" - mais lorsque vous y pensez, elles ne sont plus inattendues.
gbjbaanb
7
On peut se demander si votre code if (var != true || var != false) {devrait &&plutôt être un code .
1
Je peux facilement penser à un bool qui n'est ni vrai ni faux, mais qui reste inattendu. Dans le cas où vous dites qu'une valeur booléenne ne peut pas être autre chose, si je vérifie si une lettre est un caractère numérique et que je la convertis ensuite en valeur entière, je peux facilement penser que cette valeur entière est inférieure à 0 ou supérieure à 9, mais elle reste inattendu.
gnasher729
1
Les booléens nuls sont pris en charge en Java et en C # et ont une application réelle. Considérons une base de données contenant une liste de personnes. Après un moment, vous décidez que vous avez besoin d'un champ de genre (isMale). Null signifie "jamais demandé, donc je ne sais pas"; vrai signifie masculin et faux signifie féminin. (OK, transgenre omis pour plus de simplicité ...).
Kiwiron
@kiwiron: Une meilleure solution ne serait-elle pas d'utiliser un type d'énumération, "Homme", "Femme", "N'a pas demandé". Les énumérations sont meilleures - peuvent être étendues lorsque le besoin s'en fait sentir (par exemple, dans votre esprit asexué, hermaphrodite, "Refusé de répondre").
Mattnz
9

En réalité, votre code n’est pas plus robuste mais MOINS robuste. La finale elseest simplement un code mort que vous ne pouvez pas tester.

Dans les logiciels critiques tels que les vaisseaux spatiaux, le code mort et, plus généralement, le code non testé sont interdits: si un rayon cosmique perturbe un seul événement et que votre code mort est activé, tout est possible. Si le SEU active une partie du code robuste, le comportement (inattendu) reste sous contrôle.

mouviciel
la source
Je ne l'obtiens pas dans les vaisseaux spatiaux. Le code mort est-il interdit? c'est-à-dire que vous ne pouvez pas écrire le dernier? puisque vous ne pouvez pas le tester, vous ne pouvez pas le mettre? mais alors qu'est-ce que cela signifie "Si le SEU active une partie du code robuste, le comportement (inattendu) reste sous contrôle."
minable
5
Oui, dans l'espace, la couverture des tests logiciels critiques doit être de 100% et, par conséquent, le code inaccessible (code mort) est interdit.
mouviciel
7

Je pense que le professeur pourrait être déroutant "erreur" et "bug". Le code robuste devrait certainement avoir peu / pas de bugs. Un code robuste peut, et dans un environnement hostile, avoir une bonne gestion des erreurs (qu'il s'agisse de la gestion des exceptions ou des tests de statut de retour rigoureux).

Je conviens que l'exemple de code du professeur est ridicule, mais pas aussi ridicule que le mien.

// Assign 3 to x
var x = 3;
x = 3;   // again, just for sure
while (x < 3 or x > 3) { x = 3; }  // being robust
if (x != 3) { ... }  // this got to be an error!
David Andersson
la source
1
Le dernier, si certainement déclenché, ne nécessiterait pas vraiment beaucoup d'effort. Tout programmeur C expérimenté a vu les valeurs changer soudainement. Bien sûr, logiquement, dans un environnement mono-thread contrôlé, cela ne devrait jamais se produire. Dans la vraie vie, le code à l'intérieur du if finira par arriver. S'il n'y a rien d'utile que vous puissiez faire à l'intérieur de cela si, alors ne le codez pas! (J'ai eu une expérience amusante lors du développement d'un logiciel particulier où j'ai soulevé une exception avec des mots maudits au cas où quelque chose d'impossible se produirait ... devinez ce qui s'est passé?).
Alex
2
Histoire vraie:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
Andres F.
6

Il n'y a pas de définition convenue du code robuste , car pour beaucoup d'éléments de programmation, c'est plus ou moins subjectif ...

L’exemple donné par votre professeur dépend de la langue:

  • En Haskell, un Booleanpeut être Trueou False, il n'y a pas de troisième option
  • En C ++, un boolpeut être true, falseou (malheureusement) provient d'une distribution douteuse qui le met dans un cas inconnu ... Cela ne devrait pas arriver, mais peut, à la suite d'une erreur précédente.

Cependant, les recommandations de votre professeur occultent le code en introduisant une logique superflue pour les événements à ne pas se produire au milieu du programme principal. Je vous orienterai donc plutôt vers la programmation défensive .

Dans le cas d'une université, vous pouvez même l'augmenter en adoptant une stratégie de conception par contrat:

  • Établir des invariants pour les classes (par exemple, sizeest le nombre d'éléments dans la dataliste)
  • Établissez des conditions préalables et postérieures pour chaque fonction (par exemple, cette fonction ne peut être invoquée que si elle aest inférieure à 10)
  • Testez chacun de ceux-ci aux points d'entrée et de sortie de chacune de vos fonctions

Exemple:

class List:
  def __init__(self, items):
    self.__size = len(items)
    self.__data = items

  def __invariant(self):
    assert self.__size == len(self.__data)

  def size(self):
    self.__invariant()

    return self.__size

  def at(self, index):
    """index should be in [0,size)"""
    self.__invariant()
    assert index >= 0 and index < self.__size

    return self.__data[index]

  def pushback(self, item):
    """the subsequent list is one item longer
       the item can be retrieved by self.at(self.size()-1)"""
    self.__invariant()

    self.__data.append(item)
    self.__size += 1

    self.__invariant()
    assert self.at(self.size()-1) == item
Matthieu M.
la source
Mais le professeur a spécifiquement dit qu'il s'agissait de Java et n'a pas précisé le type de var. S'il s'agit d'un booléen, il peut être vrai, faux ou nul. Si quelque chose d'autre, il peut être inégal à la fois vrai et inégal à faux. Oui, chevauchement entre robuste, défensif et paranoïaque.
Andy Canfield
2
En C, C ++ et Objective-C, une valeur booléenne peut avoir une valeur indéterminée, comme tout autre type, mais toute affectation la définira sur true ou false et rien d'autre. Par exemple: bool b = 0; b ++; b ++; mettra b à vrai.
gnasher729
2

L'approche de votre professeur est totalement erronée.

Une fonction, ou juste un peu de code, devrait avoir une spécification qui dit ce qu’elle fait, qui devrait couvrir toutes les entrées possibles. Et le code doit être écrit de manière à ce que son comportement corresponde à la spécification. Dans l'exemple, j'écrirais la spécification assez simple comme ceci:

Spec: If var is false then the function does "this", otherwise it does "that". 

Ensuite, vous écrivez la fonction:

if (var == false) dothis; else dothat; 

et le code répond à la spéc. Alors votre professeur dit: Et si var == 42? Regardez la spécification: il est dit que la fonction doit faire "ça". Regardez le code: la fonction fait "ça". La fonction répond à la spéc.

Là où le code de votre professeur rend les choses totalement douteuses, c'est qu'avec son approche, quand var n'est ni vrai ni faux, il exécute un code qui n'avait jamais été appelé auparavant et qui n'a absolument pas été testé, avec des résultats totalement imprévisibles.

gnasher729
la source
1

Je suis d 'accord avec la déclaration de @ gnasher729: l' approche de votre professeur est totalement erronée.

Robuste signifie qu'il est résistant aux ruptures / défaillances car il fait peu d'hypothèses et est découplé: il est autonome, se définit et est portable. Cela implique également d'être adaptable à l'évolution des besoins. En un mot, votre code est durable .

Cela se traduit généralement par des fonctions courtes récupérant leurs données à partir de paramètres transmis par l'appelant et par l'utilisation d'interfaces publiques pour les consommateurs (méthodes abstraites, wrappers, indirection, interfaces de style COM, etc.) plutôt que par des fonctions contenant un code d'implémentation concret.

Vecteur
la source
0

Le code robuste est simplement un code qui gère bien les échecs. Ni plus ni moins.

Parmi les types d'échec, il en existe de nombreux types: code incorrect, code incomplet, valeurs inattendues, états inattendus, exceptions, épuisement des ressources, ... Le code robuste les gère bien.

Sparky
la source
0

Je considérerais le code que vous avez donné comme exemple de programmation défensive (du moins si j'utilise le terme). Une partie de la programmation défensive consiste à faire des choix qui minimisent les suppositions faites sur le comportement du reste du système. Par exemple, lequel de ces choix est le meilleur:

for (int i = 0; i != sequence.length(); ++i) {
    // do something with sequence[i]
}

Ou:

for (int i = 0; i < sequence.length(); ++i) {
    // do something with sequence[i]
}

(Si vous avez du mal à voir la différence, vérifiez le test de boucle: le premier utilise !=, le second utilise <).

Maintenant, dans la plupart des cas, les deux boucles se comporteront exactement de la même manière. Cependant, la première (comparaison avec !=) fait une hypothèse qui ine sera incrémentée qu’une fois par itération. Si la valeur sequence.length()est ignorée, la boucle peut continuer au-delà des limites de la séquence et provoquer une erreur.

Vous pouvez donc faire valoir que la deuxième implémentation est plus robuste: elle ne dépend pas d'hypothèses sur le fait que le corps de la boucle change i(remarque: en fait, l'hypothèse in'est jamais négative).

Pour vous motiver à ne pas faire cette hypothèse, imaginez que la boucle balaye une chaîne et effectue un traitement de texte. Vous écrivez la boucle et tout va bien. Maintenant que vos exigences changent et que vous décidez que vous devez prendre en charge les caractères d'échappement dans la chaîne de texte, vous modifiez le corps de la boucle de sorte que, si elle détecte un caractère d'échappement (par exemple, une barre oblique inversée), elle s'incrémente ipour ignorer le caractère immédiatement après l'échappement. Maintenant, la première boucle a un bogue car si le dernier caractère du texte est une barre oblique inverse, le corps de la boucle sera incrémenté iet la boucle continuera au-delà de la fin de la séquence.

John Bartholomew
la source
-1

Personnellement, je décris un code comme 'robuste' qui a celui-ci, attributs importants:

  1. Si ma mère est assise devant et travaille avec elle, elle ne peut pas casser le système

Maintenant, par pause, je veux dire soit que le système soit dans un état instable, soit qu’il provoque une exception UNHANDLED . Vous savez, parfois pour un concept simple, vous pouvez faire une définition et une explication complexes. Mais je préférerais des définitions simples. Les utilisateurs savent assez bien trouver des applications robustes. Si l'utilisateur de votre application vous envoie de nombreuses demandes concernant des bogues, une perte d'état, des flux de travail non intuitifs, etc., il y a un problème avec votre programmation.

Saeed Neamati
la source