Introduction aux design patterns Qu’est-ce qu’un design pattern Définition : C’est une solution éprouvée ... à un problème de conception logicielle récurrent ... proposée sous forme de fragment d'architecture objet. Synonymes en français : patron de conception, modèle de conception, motif de conception Historique Concept proposé par un architecte (Christopher Alexander) dans les années 70 pour apporter des solution à des problèmes courants dans le bâtiment (solidité, structure, étanchéité…) Application du concept à l’ingénierie logicielle dans les années 80 Large diffusion en 1994 grâce au livre “Design patterns: elements of reusable object-oriented software” Auteurs : “La bandes des 4” ou Gang of Four souvent abrégé en GoF Contenu : 23 architectures objet constituant des propositions de solution à autant de situations courantes. Pourquoi utiliser les design pattern Pour accélérer le processus de développement en fournissant des solutions de développement éprouvées. Pour anticiper des problématiques qui peuvent ne devenir visibles que plus tard dans la mise en œuvre. Pour améliorer la lisibilité du code en fournissant une standardisation. Classification 3 familles de design pattern selon la problématique à traiter Comment structurer son code pour faciliter l’interaction entre les objets ? ⇒ Patrons de structure (structural patterns) Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy Comment faire communiquer des objets entre eux ? ⇒ Patrons de comportement (behavioral patterns) Strategy, Chain of responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Template Method, Visitor Comment créer ou configurer des objets pour une utilisation adaptée au contexte ? ⇒ Patrons de création (creational patterns) Singleton, Abstract Factory, Builder, Factory Method, Prototype Étude du design pattern Adapter Généralités Objectif : Adapter l’interface d’une ou plusieurs classes afin de les rendre compatibles avec ce qu’attend l’utilisateur de ces classes Illustration : Étude de cas Mise en situation : On désire mettre en œuvre un ensemble de capteurs de températures pour surveiller un local technique. 2 types de capteurs sont disponibles. Chacun des capteurs dispose d’une librairie C++ pour permettre son exploitation : fichier d’entête .h + librairie statique sous forme binaire (libfoosensor.a pour le capteur FooSensor, libbarsensor.a pour le capteur BarSensor). Nous n’avons donc pas accès au code source de ces librairies. Problématique : Les 2 types de capteurs délivrant — via les méthodes getTemperature() et readTemp() de leur librairies respectives — une température dans 2 unités différentes (°C et °F), comment utiliser ces 2 types de capteur de manière uniforme ? (c’est-à-dire n’utiliser qu’une seule méthode pour acquérir une T° exprimée en °C quel que soit le type de capteur) Solution : le patron Adapter Application à notre exemple On va créer une interface commune aux 2 capteurs (→ classe CapteurTemperature) puis 2 classes intermédiaires (→ FooAdapter et BarAdapter) qui vont convertir les valeurs retournées par les méthodes présentes dans chacune des librairies de façon à se conformer à ce qui est défini dans l’interface à savoir retourner une température en °C. Code source de la démo Ce code source est à compiler sous Windows. Adapter les fichier .pro pour les compiler sur une autre plateforme Étude du patron Strategy Généralités Objectif : Définir une famille de comportements/algorithmes indépendants qui répondent à la même interface et qui sont interchangeables dynamiquement depuis le client qui les utilise. Exemple : trier des nombres par ordre croissant avec plusieurs algorithmes (bubble sort, quick sort…) Illustration : Étude de cas Mise en situation On désire faire une application qui supervise la vitesse du vent — relevée à l’Isle sur la Sorgue — en affichant sa vitesse en km/h et également son équivalent sur l’échelle de Beaufort . La vitesse du vent en m/s nous est fournie par une API Web (OpenWeatherMap ). L’affichage de la vitesse du vent dans l’IHM sera réactualisé toutes les 2 secondes. Problématique Comment vérifier que l’application affiche la bonne vitesse de vent sur l’échelle de Beaufort en fonction de la vitesse du vent renvoyée par l’API Web sachant qu’on ne peut pas changer la force du vent à notre convenance. Solution : le patron Strategy On va coder notre application de sorte à ce qu’elle utilise soit la vitesse du vent réelle retournée par l’API Web soit celle retournée par un capteur de vent simulé. Le patron de conception Strategy sera mis en œuvre pour choisir depuis l’IHM de notre application (c’est-à-dire lors de l’exécution de l’application), le capteur de vent à utiliser (réel ou simulé). Application à notre exemple Code source de la démo Pour que l’application fonctionne, fournir votre propre clé d’API OpenWeatherMap dans le fichier realwindspeedsensor.cpp de la démo. m_nr.setUrl(QUrl("http://api.openweathermap.org/data/2.5/weather?q=L'Isle-sur-la-sorgue,fr&appid=<<VOTRE_CLE_D_API>>")); Étude du patron Singleton Généralités Objectifs : assurer qu’il n’existe qu’une seule instance de la classe fournir un moyen d’obtenir cette instance unique Illustration : Étude de cas Mise en situation : On veut coder une classe qui permet de tenir un fichier journal (→ log) dans lequel différents composants d’une application peuvent — de manière optionnelle et à la demande — écrire des messages d’information ou d’erreur. Problématique : Comment s’assurer qu’un seul journal sera créé/utilisé quel que soit le composant d’où émane la 1ière demande ? Comment s’assurer que le fichier journal sera fermé lorsque le dernier des composants à l’utiliser mettra fin à la journalisation ? Solution : le patron Singleton Application à notre exemple Autres cas d’utilisation du design pattern Singleton : accès centralisé à une BDD accès centralisé à du matériel (ex. : classe de gestion d’un bus I2C) 🞄 🞄 🞄 Techniques de masquage en C/C++ Javascript