Design Patterns : à quoi ça sert et comment les utiliser ?
Le développement de programmes informatiques est au coeur de la révolution Internet qui envahit nos vies depuis désormais quelques décennies. Ecrire des lignes de code pour faire fonctionner un programme informatique est un pilier indispensable du monde moderne.
La mouvance "nocode" qui sévit depuis quelques années repose aussi sur des programmes informatiques qui eux-mêmes sont composés de lignes de code, sans doute certes un peu plus complexes.
L'homo numéricus ne fait pourtant que reproduire un schéma qui existe depuis des milliers d’années : apporter une solution à un problème donné.
La roue a été autrefois inventée pour faciliter le transport sur terre. Aujourd’hui, nous créons des applications qui nous permettent de la même façon d’augmenter la productivité, de réduire les tâches répétitives pouvant être automatisées ou même créer des nouvelles interactions entre les hommes.
Face à un problème, il y a souvent une multitude de solutions. À nous de choisir avec notre expérience, nos connaissances, celles qui semblent le mieux correspondre.
En termes d'architecture d’application, c’est ici que les design patterns (patrons de conception dans la langue de Molière) interviennent et nous permettent, avec des méthodes et des outils déjà éprouvés, d’aborder les problématiques de conception d’une application.
Design Patterns : la définition
Un patron de conception est un arrangement caractéristique de modules, reconnu comme bonne pratique pour résoudre un problème de conception d'un logiciel. Il décrit une solution standard, utilisable dans la conception de différents logiciels.
Un patron de conception est issu de l'expérience des concepteurs de logiciels. Il décrit un arrangement récurrent de rôles et d'actions joués par des modules d'un logiciel, et le nom du patron sert de vocabulaire commun entre le concepteur et le programmeur. D'une manière analogue à un motif de conception en architecture, le patron de conception décrit les grandes lignes d'une solution, qui peuvent ensuite être modifiées et adaptées en fonction des besoins.
Les patrons de conception décrivent des procédés de conception généraux et permettent de capitaliser l'expérience appliquée à la conception de logiciel. Ils ont une influence sur l'architecture logicielle d'un système informatique.
Des exemples de Design Patterns
1. Factory
Factory est un des modèles de conception les plus utilisés Ce patron permet de mettre en place une classe qui va se charger d’instancier les classes nécessaires au fonctionnement de votre application. Cela permet notamment au code source de se libérer de toute responsabilité concernant l’implémentation, la configuration et l’implémentation.
Factory est un des modèles de conception les plus utilisés. Ce patron permet de mettre en place une classe qui va se charger d’instancier les classes nécessaires au fonctionnement de votre application. Cela permet notamment au code source de se libérer de toute responsabilité concernant l’implémentation, la configuration et l’implémentation.
Ce patron de conception est utile en particulier lorsque vous devez instancier des objets qui implémentent une même interface ou classe abstraite mais dans des contextes différents. Il peut s'agir par exemple d'une classe de gestion de bases de données qui permet de manipuler plusieurs types de bases de données.
Imaginons que dans notre application, suivant le contexte, nous voulons utiliser soit une base MySQL, soit une base PostgreSQL. Nous allons utiliser le patron Factory afin d'éviter de devoir réécrire le code si on change de base de données.
Tout d’abord, l’interface :
Puis nos deux classes qui implémentent cette interface :
Et ensuite, la classe factory :
Nous pouvons maintenant utiliser cette classe dans le code de l'application en fonction de la base de donnée que nous voulons utiliser :
2. Observer
Observer est aussi un patron de conception très utilisé au sein des projets PHP : il permet de limiter la dépendance inter-objets.
Lorsqu’un événement se produit dans une application, il peut être nécessaire de produire une ou plusieurs actions en cascade. La première solution est d’écrire cette suite d’action dans une méthode. Le problème : cette classe devient donc dépendante de toutes les autres qui exécutent ces actions.
Observer permet de séparer les différentes actions. Pour cela, nous allons créer un objet qui va être observé.
Prenons comme exemple une classe User :
Puis une classe UserManager qui va s’occuper de la gestion des utilisateurs. C’est cette classe qui va est observée, elle étend donc la classe standard SplSubject :
La méthode create permet de créer un utilisateur et alerte tous les observateurs.
Créons maintenant deux classes observatrices : une qui envoie un email et une autre qui indexe un moteur de recherche. Elles doivent implémenter l’interface SplObserver et la méthode update.
Il ne nous reste plus qu’à créer notre objet observé et y attacher l’ensemble des classes qui vont l’observer :
3. Dependences Injection
Le patron de conception Dependences Injection permet de découpler les classes. Définissons deux classes : User et Address.
Dans cet exemple, si vous avez besoin de modifier la classe Address pour lui ajouter des propriétés, vous aurez besoin de modifier aussi la classe User. La classe User est donc couplée à la classe Address et perd son indépendance.
Afin de remédier à cela, utilisons l’injection de dépendance :
L’injection de dépendance, c’est donc ça : si une classe à besoin d’une autre classe (dans son constructeur ou une autre méthode, comme un setter par exemple) alors elle prend directement cette classe en paramètre et ne se préoccupe pas elle-même de l’instanciation.
Procéder ainsi permet d’écrire du code découplé et évite les interdépendances entre les différents composants de votre application ce qui les rend plus facilement réutilisables et beaucoup plus maintenables !
4. Decorator
Le patron de conception Decorator permet d’attacher dynamiquement des responsabilités à un objet grâce à l’utilisation de l’héritage.
Dans l’exemple suivant, nous avons besoin d’afficher un message dans une page HTML. Créons tout d’abord une interface commune à nos différents messages :
Créons ensuite un objet affichable qui implémente cette interface :
Nous avons besoin par la suite d’ajouter des fonctionnalités à cette classe d’affichage lui permettant par exemple d’afficher ce message en gras ou en italique. Notre premier réflexe serait de créer une classe par fonctionnalité héritant de la classe SimpleMessage. Cependant, en fonction des cas, l'héritage peut être complexe à mettre en place (de nombreuses modifications). Il se peut aussi que vous souhaitiez ajouter des responsabilités à ces objets de manière dynamique. C’est ici que nous allons faire appel aux Décorateurs.
Créons la classe décorateur qui sera une classe abstraite implémentant l’interface MessageInterface :
Ensuite, une classe par responsabilité nouvelle :
Bien évidemment, ces deux classes implémentent la méthode sendMessage définie dans l’interface précédemment créée. Grâce à notre décorateur, vous pouvez chaîner ces nouvelles fonctionnalités :
Si par la suite vous voulez ajouter de la couleur, ou gérer une taille d’affichage différente, il vous suffit de créer de nouvelles classes sur le même modèle que les deux précédentes !
5. Prototype
Prototype est un patron de conception de création : son but est de répliquer des instances prototypes via le clonage. Il est utile lorsqu’on souhaite créer une multitude de même instance : le coût en ressources du clonage est bien inférieur au coût d’instanciation.
Tout d’abord, définissons une classe abstraite :
Créons ensuite les classes concrètes qui hériteront de cette classe abstraite. Celles-ci ne possèdent qu’une seule méthode __clone() (vide) et une propriété non modifiable.
Ensuite, vient le code de création de nos différents objets :
Notes concernant Prototype :
- À ne pas utiliser pour un petit nombre d’objets, le jeu n’en vaut pas la chandelle !
- Lors de l’appel à __clone, le constructeur n’est pas appelé !
- Si vos objets sont composés d’autres objets, veillez à bien les cloner aussi pour éviter de faire pointer vos objets vers une même référence.
Il existe une multitude d’autre patrons de conception, le site DesignPatternsPHP répertorie la majorité des patrons applicables à PHP.
Mais souvenez vous du plus important : chaque design pattern a son utilité et n’est en aucun cas à utiliser tout le temps et partout. Il répond à une problématique donnée. Si cette problématique ne se présente pas à vous, pas besoin de la solutionner. 😀