Xavier Lamorlette
Sommaire :
auto
Une fonction pure :
Une classe / fonction doit avoir une et une seule responsabilité.
Une classe ne doit donc changer que pour une seule raison.
Cela permet :
Une classe doit être ouverte à l'extension mais fermée à la modification : elle ne doit être modifiable que par extension.
L'objectif est de ne pas toucher le code existant pour ne pas introduire de bug.
Cela force à un bon usage de l'abstraction et du polymorphisme.
On doit pouvoir utiliser une instance d'une classe dérivée à la place d'une de la classe de base sans le savoir, sans que cela modifie la cohérence du programme.
Ce principe de substitualité permet de dire si l'héritage est cohérent de point de vue fonctionnel des clients.
Programmation par contrat :
Il vaut mieux des interfaces spécifiques pour chaque client plutôt qu'une seule interface générale.
Un client ne doit pas dépendre de méthodes qu'il n'utilise pas.
Ce principe est proche de celui de responsabilité unique.
Cela permet :
Il faut dépendre des abstractions et non des implémentations.
Les modules de haut niveau ne doivent pas dépendre de ceux de bas niveau. Tous doivent dépendre d'abstractions.
Les abstractions ne doivent pas dépendre des détails mais le contraire.
La dépendance est un service qui fait partie de l'état de l'objet.
Un objet fourni à un autre une dépendance, plutôt que de laisser l'objet la construire ou la trouver lui-même.
Ça permet le découplage.
L'injection peut-être faite via le constructeur ou un setteur.
De Sandi Metz dans 99 Bottles of OOP.
Règles à appliquer dans l'ordre :
Étapes d'une transformation de code:
auto
Il faut limiter l'utilisation d'auto
car :
Contrairement aux “general-use types” (comme les types natifs tel int
et float
), il faut utiliser des types dont le nom explicite l'usage.
Cela peut être fait simplement par l'utilisation d'alias.
Pour que cela génére des erreurs de compilation, on peut utiliser des struct.
Une méthode doit être, de manière exclusive :
Il est cependant parfois utile de faire les deux. Par exemple : stack::pop()
.
GRASP signifie : General Responsibility Assignment Software Principles.
Entre deux options d'architecture, il faut choisir celle qui a le niveau de couplage le plus faible.
Organiser les responsabilités de différentes parties du code autour d'interfaces stables permet de les protéger mutuellement contre leurs variations.
Introduire un composant entre deux parties du code permet d'absorber les impacts de leurs changements.
L'essence du polymorphisme (dynamique, statique, etc.) est de découpler le client des différentes manières (implémentations) de réaliser une tâche.
Une partie de code (fonction, classe, module) doit se concentrer sur une seule tâche.
Il faut assigner une responsabilité à la classe qui a les données nécessaires à cette responsabilité : si une fonction utilise certains paramètres, elle doit être une méthode de la classe qui contient ces paramètres.
Cela permet de conserver la localité et l'encapsulation des données.
Une classe doit être en charge d'en créer une autre si :
Si une responsabilité ne peut être logiquement assignée à une classe du domaine applicatif, il faut créer une nouvelle classe en dehors de celui-ci pour la prendre en charge de manière cohérente.
Par exemple, un controller d'une interface graphique (cet exemple est le neuvième principe GRASP).
Chaque partie de code ne doit concerner qu'un seul niveau d'abstraction : dans une partie de code (fonction, classe, méthode, module, etc.), il ne faut pas mélanger différents niveaux d'abstraction.
La dernière mise à jour de cette page date de juillet 2021.
Le contenu de ce site est, en tant qu'œuvre originale de l'esprit, protégé par le droit d'auteur.
Pour tout commentaire, vous pouvez m'écrire à xavier.lamorlette@gmail.com.