Plus de complexité

J'ai entendu une telle expression que pour devenir programmeur, il faut être paresseux. Mais parfois, la paresse dans la programmation entraîne une terrible dette technique. Dans mon article sur la SRP, j'ai mentionné que la violation de ce principe peut conduire à une augmentation de la complexité ou même à sa multiplication. Un de mes collègues a produit un exemple intéressant, et avec mon aide, j'ai décidé de montrer à quoi il ressemble.



Décidons quelle est cette complexité excessive. Mais d'abord, parlons de son contraire, de la complexité des exigences. Par exemple, il est nécessaire de calculer le salaire de l'employé à partir du taux horaire et des heures travaillées. Et, si un employé travaille dans l'entreprise depuis plus de cinq ans, accumulez une prime. Ce «si» vient des exigences et ne peut être évité. Sous une forme ou une autre, il deviendra un élément de complexité dans le code d'application, très probablement sous la forme d'un opérateur conditionnel «si». Mais parfois, la complexité ne procède pas des exigences, mais découle de l'approche du développeur pour résoudre le problème.

L'opérateur «si», des modèles tels que «stratégie», les méthodes polymorphes ne sont pas une liste complète des techniques de programmation qui peuvent contenir cette complexité excessive. Personnellement, je suis d'ailleurs toujours contre l'utilisation de modèles par les développeurs simplement parce qu'ils le peuvent, et non pour résoudre un problème spécifique.

Voici un exemple simple. Cela peut sembler fictif, mais ce n'est pas le cas. Ce n'est même pas simplifié, c'est sous cette forme que je l'ai rencontré lors d'une revue de code il y a quelques années. À deux endroits dans le code, il y a eu des appels à la même fonction mais avec un paramètre booléen différent:

// first place doSomething(true); // second place doSomething(false); 

Des conceptions similaires semblent toujours suspectes et cette fonction ne m'a pas déçu. Ce paramètre a été transmis dans le seul but d'être vérifié à l'intérieur de cette fonction:

 doSomething(flag: boolean): void { if(flag) { // do first thing } else { // do second thing } } 

Ce contrôle peut être décrit comme «si j'ai été appelé de l'endroit A, nous faisons une chose, sinon j'ai été appelé de l'endroit B, nous faisons une autre chose». Ce drapeau, ce «si» est le sujet de toute cette note. La complexité ne vient pas des exigences de l'entreprise. Naturellement, j'ai recommandé de changer le code comme suit:

 // first place doFirstThing(); // second place doSecondThing(); //method is split into 2 parts each having their own responsibility doFirstThing(): void { // do first thing } doSecondThing(): void { // do second thing } 

C'est tout, il n'y a plus de complexité. C'est là que le développeur ne doit pas être trop paresseux et écrire une autre signature de fonction.

Ici, vous pouvez vous exclamer: "Mais ce n'est qu'un" si "", ou: "Cette violation est évidente, qui écrit le code comme ça?" Et voici le deuxième exemple. Cela montre qu'il peut être sensiblement plus difficile de voir la violation, et aussi que le coût de cette violation peut être plus d'un «si». Comme dans le premier exemple, la fonction est utilisée à deux endroits:

 // first place checkValidity(obj); // second place checkValidity(arrayOfObjs); 

La méthode, suivant son nom, vérifie la validité de l'objet. Cependant, il n'était pas évident qu'il puisse également vérifier la validité d'un tableau d'objets. J'ai modifié les noms de variables pour souligner cette violation. La méthode ressemble à ceci:

 checkValidity(parm: MyType | MyType[]): void { if(Array.isArray(parm)) { parm.forEach(p => checkValidity(p)); } else { // here the object gets checked // and conditional exception is thrown } } 

Ça y est. Un si devient beaucoup de si. Si le tableau contient 100 objets, ce «si» s'exécutera 101 fois. Et sur des données réelles, nous pourrions y avoir 30 000 objets, ce qui représente déjà une perte de performances impressionnante.

Evidemment, suivant le principe de la responsabilité exclusive, cette méthode doit être refactorisée pour obtenir 2 méthodes:

 checkItemsValidity(parms: MyType[]): void { parms.forEach(p => checkItemValidity(p)); } checkItemValidity(parm: MyType): void { // here the object gets checked // and conditional exception is thrown } 

En conséquence, vous devez également ajuster les points d'appel.

Il est intéressant de noter que les exemples que j'ai donnés dans l'article sur la SRP ont conduit à une augmentation du SLOC, les mêmes exemples, au contraire, ont conduit à une légère diminution de celui-ci, ainsi qu'à l'amélioration attendue de la qualité du code.

C’est tout. Juste quelques exemples simples pour démontrer le principe le plus important d'un bon code.

Source: https://habr.com/ru/post/fr457018/


All Articles