Je me retrouve à écrire beaucoup de code comme ceci:
int myFunction(Person* person) {
int personIsValid = !(person==NULL);
if (personIsValid) {
// do some stuff; might be lengthy
int myresult = whatever;
return myResult;
}
else {
return -1;
}
}
Cela peut devenir assez compliqué, surtout si plusieurs vérifications sont impliquées. Dans de tels cas, j'ai expérimenté d'autres styles, comme celui-ci:
int netWorth(Person* person) {
if (Person==NULL) {
return -1;
}
if (!(person->isAlive)) {
return -1;
}
int assets = person->assets;
if (assets==-1) {
return -1;
}
int liabilities = person->liabilities;
if (liabilities==-1) {
return -1;
}
return assets - liabilities;
}
Je suis intéressé par des commentaires sur les choix stylistiques ici. [Ne vous inquiétez pas trop des détails des déclarations individuelles; c'est le flux de contrôle global qui m'intéresse.]
coding-style
language-agnostic
William Jockusch
la source
la source
Réponses:
Pour ce type de problème, Martin Fowler a proposé un modèle de spécification :
Les sons ci-dessus sont un peu élevés (du moins pour moi), mais quand je l'ai essayé dans mon code, cela s'est bien passé et s'est avéré facile à mettre en œuvre et à lire.
De mon point de vue, l'idée principale est d'extraire du code qui effectue les vérifications dans des méthodes / objets dédiés.
Avec votre
netWorth
exemple, cela pourrait ressembler à ceci:Votre cas semble assez simple de sorte que toutes les vérifications semblent OK pour tenir dans une liste simple dans une seule méthode. Je dois souvent me séparer de plusieurs méthodes pour améliorer la lecture.
Je regroupe / extrait généralement des méthodes liées aux "spécifications" dans un objet dédié, bien que votre cas semble OK sans cela.
Cette question sur Stack Overflow recommande quelques liens en plus de celui mentionné ci-dessus: Exemple de modèle de spécification . En particulier, les réponses suggèrent Dimecasts «Learning the Specification pattern» pour une procédure pas à pas d'un exemple et mentionnent le document «Spécifications» rédigé par Eric Evans et Martin Fowler .
la source
Je trouve plus facile de déplacer la validation vers sa propre fonction, cela aide à garder l'intention des autres fonctions plus propre, donc votre exemple serait comme ça.
la source
if
entréevalidPerson
? Revenez simplement à laperson!=NULL && person->isAlive && person->assets !=-1 && person->liabilities != -1
place.Une chose que j'ai particulièrement bien fonctionnée est l'introduction d'une couche de validation dans votre code. Vous avez d'abord une méthode qui effectue toute la validation désordonnée et renvoie des erreurs (comme
-1
dans vos exemples ci-dessus) en cas de problème. Une fois la validation terminée, la fonction appelle une autre fonction pour effectuer le travail réel. Maintenant, cette fonction n'a pas besoin de faire toutes ces étapes de validation car elles devraient déjà être effectuées. C'est-à-dire que la fonction de travail suppose que l'entrée est valide. Comment devez-vous gérer les hypothèses? Vous les affirmez dans le code.Je pense que cela rend le code très facile à lire. La méthode de validation contient tout le code désordonné pour traiter les erreurs côté utilisateur. La méthode de travail documente proprement ses hypothèses avec des assertions et n'a alors pas à travailler avec des données potentiellement invalides.
Considérez cette refactorisation de votre exemple:
la source