ELEPHANT technologies, l’ESN locale et à taille humaine spécialisée sur 2 métiers : le développement et le pilotage autour de 4 expertises : hardware, embarqué, software et web.

 

Aujourd’hui, c’est l’un de nos elephantgénieurs, spécialisé en développement web qui nous parle des object calisthenics ! Dans cet article, nous vous parlons du Clean code et également des 9 règles d’or pour comprendre le fonctionnement de l’outil et pouvoir écrire le code.

 

Let’s go !

 


 

Le Clean code


Un code définis comme propre est toujours un peu subjectif : certains développeurs pourront considérer un code comme « clean » et d’autres non. La notion est un peu vaste. 

 

Un code propre sera plutôt rattaché au principe d’être compréhensible de façon intuitive et facile à modifier. Autrement dit, il doit être indépendant de son auteur et facilement maintenable.

 

Pour pouvoir atteindre cet objectif, il existe plusieurs grands principes de bases dont le principe dit SOLID. Sans rentrer dans les détails, ce principe qui en réalité en regroupe  5 se concentre sur la gestion des dépendances. Les objets Calisthenics sont une solution rendant cette gestion possible.



Les object Calisthenics, quèsaco ?


Vous l’aurez compris, il s’agit d’un outil parmi d'autres pour écrire du code plus compréhensible, maintenable et extensible. Les objets Calisthenics ont été introduits par Jeff Bay dans The ThoughtWorks Anthology. Le mot Calisthenics est dérivé du grec, et signifie « exercices » dans le contexte de la gymnastique. 

 

Derrière ce nom, il existe 9 règles à suivre à l’écriture du code. Certaines de ces règles sont assez déroutantes car contraire à ce que l’on nous enseigne ou à nos habitudes de développement les plus ancrées. L’idée est donc de suivre ces règles autant que possible, sans pour autant les prendre au pied de la lettre, d’autant si on n’est pas encore très à l’aise dessus. C’est plutôt en s’efforçant de les appliquer au mieux que nous changerons naturellement notre façon d’écrire du code. ✔️




Les 9 règles
 

  • Un seul niveau d’indentation par méthode

En plus de facilité la lecture du code, cette règle permet surtout de s’assurer qu’une méthode ne fait qu’une et une seule tâche. Elle rendra également les méthodes plus facilement réutilisables.


Exemple :
 

class Board {

    public String board() {

        StringBuilder buf = new StringBuilder();

 

        // 0

        for (int i = 0; i < 10; i++) {

            // 1

            for (int j = 0; j < 10; j++) {

                // 2

                buf.append(data[i][j]);

            }

            buf.append("\n");

        }

 

        return buf.toString();

    }

}


➜ Devient :

 

class Board {

    public String board() {

        StringBuilder buf = new StringBuilder();

 

        collectRows(buf);

 

        return buf.toString();

    }

 

    private void collectRows(StringBuilder buf) {

        for (int i = 0; i < 10; i++) {

            collectRow(buf, i);

        }

    }

 

    private void collectRow(StringBuilder buf, int row) {

        for (int i = 0; i < 10; i++) {

            buf.append(data[row][i]);

        }

 

        buf.append("\n");

    }

}

 

 

  • Ne pas utiliser de « else »

L'utilisation du « else » oblige à lire du code imbriqué, avec donc plus de niveaux d'indentation, alors qu'il est possible de s’en passer dans la plupart des cas.

Il faut préférer l’utilisation d’une ligne d’exécution principale avec quelques cas spéciaux.

Le moyen le plus simple pour éviter le « else » est d’utiliser un « early return »


Par exemple :

 

public void login(String username, String password) {

    if (userRepository.isValid(username, password)) {

        redirect("homepage");

    } else {

        addFlash("error", "Bad credentials");

 

        redirect("login");

    }

}

 

➜ Devient :

 

public void login(String username, String password) {

    if (userRepository.isValid(username, password)) {

        return redirect("homepage");

    }

 

    addFlash("error", "Bad credentials");

 

    return redirect("login");

}


Pour les cas plus complexes, se ranger dans le polymorphisme.

Enfin, pour exprimer un résultat sans valeur, le pattern Null Object est une meilleure solution.


 

  • Encapsuler tous les types primitifs (et les String) dans des objets

Lorsque des primitifs sont utilisés, il faut s’assurer que les données qu’elles valorisent n’ont pas de comportement qui lui sont propres. Sinon, ces données finiront dispersées dans le code. Créer un objet à la place aide à représenter cette intention et facilite la lecture partout où l’objet est utilisé.

 

Par exemple, un code postal est souvent représenté dans un String ou un primitif. Or, un code postal a une signification qui lui est propre, puisque sa composition est dictée par des règles. Cette intelligence doit être portée par son objet dédié.

Pour cela, il convient de suivre les principes suivants :

➜ Pas de types primitifs en arguments de méthode.

➜ Les fonctions ne peuvent pas retourner de types primitifs.

➜ Pour chaque type primitif nécessaire, créer une nouvelle classe


 

  • Des classes qui englobent des Collections

Il faut créer une nouvelle classe pour chaque collection, ou bien dit autrement, une classe qui contient comme attribut une Collection ne doit contenir aucun autre attribut.

 

Un corolaire de cette règle est de ne pas avoir de Collections en arguments de méthode. La logique sous-jacente rejoint celle de la règle 3.


 

  • Un seul point par ligne

L'objectif ici n'est pas forcément d'avoir un code joliment formaté mais plutôt de respecter la loi de Demeter : "Ne parlez qu'à vos amis immédiats".


L’idée sous-jacente est donc de demander à un objet de faire une action, plutôt que de lui demander sa représentation interne.

Par exemple, au lieu d’avoir : 
Chien.Corps.Queue.remuer(), le code serait plutôt de la forme Chien.exprimerJoie()


 

  • Ne pas utiliser d’abréviations

Une abréviation est souvent déroutante et complique la lisibilité aux autres développeurs. Mais également, il peut être révélateur d’une mauvaise approche. En effet, s’il paraît compliqué de nommer une variable ou une méthode de manière simple, alors il est possible que cela vienne du fait que la fonction fait trop de choses, que le contenu de la variable n’est pas clair, etc...
 

 

  • Toutes les entités doivent être le plus petit possible

De manière générale, on peut retenir ces valeurs :
 

✔️ 10 fichiers par package

✔️ 50 lignes par classe

✔️ 5 lignes par méthode

✔️ 2 arguments par méthode

 

Ces valeurs sont évidemment arbitraires. Elles ne sont pas là pour être respectées à l’unité près. La volonté est de gagner en lisibilité, mais surtout de contraindre le développeur à limiter les responsabilités des entités et garder un ensemble cohérent. 

 

 

  • Les classes ne doivent pas contenir plus de deux variables d'instance

Une fois encore, la valeur est totalement arbitraire. Il s’agit peut-être de la règle la plus difficile à suivre parmi les  9. Cependant, elle est fortement liée à la règle 3.


L’intérêt est de découpler les classes au maximum. Le bénéfice est d’avoir moins de dépendances et de faciliter les tests unitaires.

 

  • Ne pas utiliser les getter/setter

Celle-ci aussi peut paraître très désorientante car elle fait opposition, pour la plupart d’entre nous, à nos basiques d’apprentissage sur la programmation orientée objet. Pourtant, elle s’accorde parfaitement au principe « Tell, don’t ask », qui vise à placer l’intelligence au sein des objets, et s’accorderait donc plus dans la philosophie de la programmation orientée objet que nos vieilles habitudes.

 

Voici un exemple tout simple :
 

Date dateNaissance = sebastien.getDateNaissance();

Int age = DateUtils.giveAgeFromAnnee(dateNaissance) ;

if (age >= 18) {

    faireQuelqueChosePourAdulte(sebastien);

}

 

En suivant cette règle, on aurait plutôt :

 

if (sebastien.isAdult()) {

    faireQuelqueChosePourAdulte(sebastien);

}

 

 

➜ Non seulement le code est plus lisible, mais en plus toute l’intelligence et la responsabilité de la détermination de l’âge est replacé dans l’objet représentant une personne, plutôt que d’être réparti entre un service et une classe « utils » un peu fourre-tout.


 

En conclusion 

 

Certaines règles sont plus faciles à suivre que d'autres et c’est compréhensible de ne pas être forcément à l’aise avec certains principes. Elles demandent certainement un peu de pratique pour être correctement assimilées. Il est plus facile de démarrer avec les règles 1, 5 et 9 et permettent déjà d’engranger beaucoup de bénéfices dans la clarté du code. 💡

 


Si cet article vous intéresse, n’hésitez pas à allez jeter un coup d’oeil de l’article de Baptiste sur le Green Code 👨‍💻

 


Sources :

https://www.ionos.fr/digitalguide/sites-internet/developpement-web/clean-code-avantages-principes-et-exemples/

https://www.invivoo.com/lart-clean-code-environnement-java/

https://www.jimmyklein.fr/les-objets-calisthenics/

https://medium.com/@davidsen/clean-code-object-calisthenics-f6f4dec07c8b

https://williamdurand.fr/2013/06/03/object-calisthenics/

https://blog.avenuecode.com/object-calisthenics-principles-for-better-object-oriented-code

https://www.javacodegeeks.com/2015/11/tell-dont-ask.html