Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Black Friday: -25€ dès 75€ sur les livres en ligne, vidéos... avec le code BWEEK25. J'en profite !
  1. Livres et vidéos
  2. Python 3
  3. Motifs de conception
Extrait - Python 3 Traitement de données et techniques de programmation
Extraits du livre
Python 3 Traitement de données et techniques de programmation
2 avis
Revenir à la page d'achat du livre

Motifs de conception

Définition

1. Positionnement par rapport à la notion d’objet

Un objet ne fait pas l’application. Ce qui compte autant que les mécanismes permettant de travailler sur les objets, ce sont ceux qui permettent de gérer la manière dont ils interagissent en réponse à une problématique et à destination d’une fonctionnalité.

Pour ce faire, on utilise, consciemment ou non, des motifs de conception qui, chacun, correspondent à une méthode pour faire interagir des objets entre eux. Il y en a de plusieurs sortes, destinées à gérer des problématiques qui peuvent sembler parfois similaires mais qui ont toujours en réalité un contexte ou une destination particulière. Ils sont tellement nombreux qu’il faudrait un livre uniquement pour les présenter tous et cela n’aurait qu’un intérêt limité.

La plupart d’entre eux sont conçus pour répondre à une problématique précise. Ils sont la synthèse de retours d’expérience importants et significatifs et sont décrits avec précision par l’utilisation de plusieurs objets dont on décrit les rôles et les interactions.

Leur connaissance permet de standardiser la conception logicielle, de mettre en place des outils pour reproduire les bonnes solutions et d’améliorer...

Motifs de création

1. Singleton

Un singleton est, en mathématiques, un ensemble ne contenant qu’un seul élément. En informatique, il s’agit d’une classe ne possédant qu’une seule instance.

>>> class Singleton: 
...     instance = None 
...     def __new__(cls): 
...         if cls.instance is None: 
...             cls.instance = object.__new__(cls) 
...         return cls.instance 
...   
>>> object() is object(), Singleton() is Singleton() 
(False, True) 

Il faut noter que, la plupart du temps, les langages de programmation utilisent le singleton pour pallier le fait que leur modèle objet n’est pas suffisamment souple pour pouvoir gérer ce que Python fait déjà à l’aide de ses méthodes de classes.

Ainsi, l’utilisation d’un singleton en Python est extrêmement rare.

2. Fabrique

Présentation de la problématique

Lorsque l’on a une classe mère abstraite et plusieurs classes filles, la classe mère abstraite permet de capitaliser les comportements communs à toutes les filles. Pour les langages statiquement typés, une fonctionnalité utile est de potentiellement travailler avec des objets déclarés comme étant du type de la mère de manière à ce que les comportements puissent être homogénéisés. En clair, que l’on ne soit pas obligé de dupliquer du code autant de fois qu’il y ait de filles.

Pour cela, on utilise une fabrique qui va alors prendre en paramètre les données nécessaires à la construction de l’objet, déterminer à partir de critères déterministes la classe fille à instancier, créer cette instance, et la renvoyer. Cette instance portera alors le type de sa mère (voir notion de polymorphisme).

Solution

Python est un langage à typage dynamique. Il ne dispose pas de contraintes lui imposant de travailler avec un type de données particulier - à moins que le développeur l’ait lui-même spécifié dans son code - et il utilise naturellement la notion de « Duck Typing ».

En ce sens, les problématiques purement techniques réglées...

Motifs de structuration

1. Adaptateur

Présentation de la problématique

Pour avoir des traitements génériques, lorsque l’on conçoit une architecture, la solution permettant de disposer d’une interface commune et de créer les objets qui vont fournir cette interface est l’idéal.

Seulement, on travaille rarement uniquement avec des objets que l’on a conçus, on travaille également avec des bibliothèques tierces, ou des objets conçus préalablement qui sont adaptés à une problématique autre que la nôtre.

Dans tous les cas, il n’est pas possible de reprendre ces objets pour les faire entrer dans un moule qui satisfait parfaitement nos besoins.

Dans ce cadre-là, une solution largement diffusée est de créer des adaptateurs qui vont adapter le comportement des objets que l’on a à une interface unique.

Solution

Voici un exemple de classes qui sont parfaitement adaptées à une certaine utilisation et que nous souhaitons reprendre, mais utiliser d’une manière générique :

>>> class Chien: 
...     def aboyer(self): 
...         print('Ouaff') 
...  
>>> class Chat: 
...     def miauler(self): 
...         print('Miaou') 
...  
>>> class Cheval: 
...     def hennir(self): 
...         print('Hiiii') 
...  
>>> class Cochon: 
...     def grogner(self): 
...         print('Gruik') 
... 

Notre souhait est de faire « parler » ces animaux d’une manière générique.

Voici une classe, qui correspond à l’interface souhaitée :

>>> import abc 
>>> class Animal(metaclass=abc.ABCMeta): 
...     @abc.abstractmethod 
...     def faireDuBruit(self): 
...         return 
... 

On pourrait alors reprendre les quatre classes précédentes et les réécrire avec la même méthode, mais cela entraînerait une perte au niveau sémantique alors que c’est potentiellement utile pour d’autres utilisations.

Python...

Motifs de comportement

1. Chaîne de responsabilité

Présentation de la problématique

Le motif de conception nommé chaîne de responsabilité permet de créer une chaîne entre différents composants qui traitent une donnée. Ainsi chaque composant reçoit une donnée, la traite s’il le peut, et la transmet au composant suivant dans la chaîne, le tout sans se préoccuper de savoir si le message va intéresser son successeur ou pas.

Solution

Voici un composant autonome qui gère le traitement ou non d’une donnée en fonction de conditions qui lui sont passées à l’initialisation :

>>> class Composant: 
...     def __init__(self, name, conditions): 
...         self.name = name 
...         self.conditions = conditions 
...         self.next = None 
...     def setNext(self, next): 
...         self.next = next 
...     def traitement(self, condition, message): 
...         if condition in self.conditions: 
...             print('Traitement du message %s par %s' %  
(message, self.name)) 
...         if self.next is not None: 
...             self.next.traitement(condition, message) 
... 

Voici comment créer trois composants :

>>> c0 = Composant('c0', [1, 2]) 
>>> c1 = Composant('c1', [1]) 
>>> c2 = Composant('c2', [2]) 

Comment créer la chaîne de dépendance :

>>> c0.setNext(c1) 
>>> c1.setNext(c2) 

Et le résultat lorsque l’on donne une condition et un message :

>>> c0.traitement(1, 'Test 1') 
Traitement du message Test 1 par c0 
Traitement du message Test 1 par c1 
>>> c0.traitement(2, 'Test 2') 
Traitement du message Test 2 par c0 
Traitement du message Test 2 par c2 

Conséquences

Cette méthodologie est un moyen simple de créer un découplage entre fonctionnalités séquentiellement exécutées.

2. Commande

Présentation de la problématique

Le modèle...

ZCA

1. Rappels

La ZCA est la Zope Component Architecture et est un ensemble de bibliothèques indépendantes qui permettent de créer une architecture entre composants.

2. Adaptateur

Déclaration

Voici, déclarés conformément aux usages de la ZCA, deux interfaces et deux objets :

>>> from zope.interface import Interface 
>>> from zope.interface import Attribute 
>>> from zope.interface import implements 
>>> class Ichien(Interface): 
...     nom = Attribute("""Nom du chien""") 
...     def aboyer(filename) 
...         """Méthode permettant de le faire aboyer"""  
...  
>>> class Chien(object): 
...     implements(IChien) 
...     nom = u'' 
...     def __init__(self, nom): 
...         self.nom = nom 
...     def aboyer(self): 
...         """Méthode permettant de le faire aboyer""" 
...         print('Ouaff') 
...  
>>> class Ichat(Interface): 
...     nom = Attribute("""Nom du chat""") 
...     def miauler(filename): 
...         """Méthode permettant de le faire miauler"""  
...  
>>> class Chat(object): 
...     implements(IChat) 
...     nom = u'' 
...     def __init__(self, nom): 
...         self.nom = nom 
...     def miauler(self): 
...         """Méthode permettant de le faire miauler""" 
...         print('Miaou') 
... 

L’idée de cet exemple est d’adapter ces deux objets au sein d’une seule et même classe dont on va commencer par créer une interface. Cette adaptation est simplement réalisée par l’utilisation de la fonction adapts.

Voici...