Améliorer son code par des cas réels

Lorsque l’on code et que l’on est débutant ou intermédiaire, il y a des bonnes chances de faire du code touffu ou en spaghetti, illisible, et souvent buggé. Nous allons voir avec des exemples concrets comment à partir d’un code issu du monde réel, on peut améliorer la lisibilité du code, la concision, et l’efficacité tout en évitant des bugs.

Lecture d’un tableau d’objet en Javascript

L’exemple ci-dessous est tiré d’une application Handlebars, le snippet lit un tableau d’objet en deux dimensions.

if(enfantsObject.EnfantsComm != null || enfantsObject.EnfantsP1 != null || enfantsObject.EnfantsP2 != null){
            if(Object.keys(enfantsObject).length){
                $.each(enfantsObject, function(index,value){
                    if(value != null && value != "{}" && jQuery.isNumeric( value )){
                        $.each(value, function(index2, value2){
                            if(value2.pension !== undefined){
                                montPens += Number(value2.pension);
                            }
                        })
                    }
                })
            }
        }

Le model à lire était un tableau d’objets avec les propriétés suivantes, un seul niveau d’imbriquement. Il y a plusieurs choses qui me surprennent, le fait d’utiliser la fonction de Jquery each, qui sert à itérer dans les éléments du DOM (les tags HTML), elle est ici utilisée alors qu’elle ne devrait pas l’être, inapproprié.

Le problème c’est que les propriétés sont changeante, elles peuvent ne pas être présentessuivant la façon dont l’objet a été initialisé. Voir l’exemple ci-dessous :

Dans l’exemple ci-dessus, les propriétés pension et pensionDate ne sont pas présentes ! Mauvaise pratique qui va nous compliquer le code, soit on retravaille l’initialisation, soit on checke les objets quand ils sont utilisés.

Ensuite il y a le Object.keys, elle est utilisée ici pour détecter que l’objet possède des clés (enfantCommuns, enfantP1, etc). Ceci n’est pas du tout stable, c’est en amont que les choses devraient être faites, notamment en initialisant proprement les différents propriétés de l’objet enfantsObject, y mettre un objet vide nous assure de la présence de la propriété et ne pas à avoir faire ce check inutile.

Première étape on simplifie le code

Si vous voulez améliorer votre code, mais que vous ne savez pas par où commencer, choisissez la lisibilité, qui doit être l’ultime but recherché, pas de code intelligent qui marche mais difficile à lire pour les autres développeur. Quitte à faire une simple boucle for au lieu de faire un map comme en programmation déclarative.

Ci-dessous, j’ai scindé en trois parties (attention il ya duplication de code similaire), ce qui n’est pas bon, mais cette étape intermédiaire nous permettra de refactorer en une fonction réutilisable.

J’ai du faire varier les différents cas sur l’interface pour voir les cas limites, et vous verrez un check pour s’assurer que c’est un tableau, en effet, quand il n’y a pas d’enfant présent, on n’a pas un tableau vide mais un objet ! (pourquoi ne pas revoir l’initialisation vous me direz? pour des impératifs de budget tout simplement)

Preuve que répéter du code n’est pas bon, parfois j’oublie de renommer à certains endroits et le programme plante.

        var pensionEnfantscommun = 0;
        var pensionEnfantPers1 = 0;
        var pensionEnfantPers2 = 0;

        if(enfantsObject.EnfantsComm != null && Array.isArray(enfantsObject.EnfantsComm)){
            for(i=0;i<enfantsObject.EnfantsComm.length;i++){
                if(enfantsObject.EnfantsComm[i].pension != undefined && enfantsObject.EnfantsComm[i].pension != ""){
                    pensionEnfantscommun += parseInt(enfantsObject.EnfantsComm[i].pension);
                }
            }    
        }

        if(enfantsObject.EnfantsP1 != null && Array.isArray(enfantsObject.EnfantsP1)){
            for(i=0;i<enfantsObject.EnfantsP1.length;i++){
                if(enfantsObject.EnfantsP1[i].pension != undefined && enfantsObject.EnfantsP1[i].pension != ""){
                    pensionEnfantPers1 += parseInt(enfantsObject.EnfantsP1[i].pension);
                }
            }    
        }

        if(enfantsObject.EnfantsP2 != null && Array.isArray(enfantsObject.EnfantsP2)){
            for(i=0;i<enfantsObject.EnfantsP2.length;i++){
                if(enfantsObject.EnfantsP2[i].pension != undefined && enfantsObject.EnfantsP2[i].pension != ""){
                    pensionEnfantPers2 += parseInt(enfantsObject.EnfantsP2[i].pension);
                }
            }    
        }

On vérifie que tout fonctionne (en tant que développeur il faut toujours bien tester ce que l’on fait). Maintenant nous pouvons faire une fonction réutilisable.

Seconde étape on refactore avec des fonctions

        // sous fonction 
        const majorationPension = function(enfant){
            if(enfant.pensionDate != undefined && enfant.pensionDate != ""){
                return Math.min(parseInt(enfant.pension) * 1.25,PARAM.SEUIL_JUGEMENT) 
            } else {
                return parseInt(enfant.pension)
            }
        }

        // fonction locale de calcul de fonction
        const sumPension = function(arrayEnfants){
                            const pensionEnfants = 0
                                if(arrayEnfants != null && Array.isArray(arrayEnfants)){
                                            for(i=0;i< arrayEnfants.length;i++){
                                                if(arrayEnfants[i].pension != undefined && arrayEnfants[i].pension != ""){
                                                    pensionEnfants += majorationPension(arrayEnfants[i]);
                                                }
                                            }    
                                        }
                                        return pensionEnfants
                                    }
        var montPens = 0;
        var pensionEnfantscommun = 0;
        var pensionEnfantPers1 = 0;
        var pensionEnfantPers2 = 0;

        pensionEnfantscommun = sumPension(enfantsObject.EnfantsComm)
        pensionEnfantPers1 = sumPension(enfantsObject.EnfantsP1)
        pensionEnfantPers2 = sumPension(enfantsObject.EnfantsP2)

J’ai ajouté une sous fonction majorationPension parce qu’il était nécessaire de faire un filtrage, et on n’oublie pas le parseInt, qui permet de convertir en véritable entier, sinon on aurait des erreurs de type NaN (not a number)

Notez l’utilisation de const pour des variable locales, une bonne pratique à prendre.

Conclusion

Maintenant on a sauvé quelques lignes, et surtout obtenu un code plus lisible pour la maintenance ou les évolutions plus tard.

Retour en haut