Modèles de Conception Structuraux

Les patterns structuraux définissent comment organiser les classes d'un programme dans une structure plus large (séparant l'interface de l'implémentation).

Adaptateur 

Adaptateur (Adapter) : permet de convertir l'interface d'une classe en une autre interface que le client attend. L’adaptateur fait fonctionner ensemble des classes qui n'auraient pas pu fonctionner sans lui, à cause d'une incompatibilité d'interfaces

Bridge

  • Pont (Bridge)

Composite

  • Objet composite (Composite)

Décorateur 

Décorateur (Decorator) : permet d'attacher dynamiquement de nouvelles responsabilités à un objet. Les décorateurs offrent une alternative assez souple à l'héritage pour composer de nouvelles fonctionnalités.

```console
Catalogue

<< interface >>

Collection

+Ajouter(obj:object):void
+Supprimer(obj:object):void
+Lire(indice:int):object

Livrable

CollectionLectureSeule
+Ajouter(obj:object):void
+Supprimer(obj:object):void
+Lire(indice:int):object

*

contient
Delegue à
+ Livrables
Ajouter et Supprimer lancent une exception à l'exécution
```

Cela peut paraître satisfaisant, mais en réalité, nos principes de conception ne sont pas tous satisfaits. En particulier, notre décorateur ne répond pas au principe de substitution de Liskov puisque l'utilisation d'une CollectionLectureSeule en lieu et place d'une Collection n'est pas neutre et ne peut pas être traitée sans précaution supplémentaire: l'utilisateur typique d'une Collection ne sera pas prêt à rattraper les exceptions que lèverait une CollectionLectureSeule! En décorant ainsi la Collection, nous avons donc rompu le contrat qui la lie à ses utilisateurs (celui de répondre aux services Ajouter() et Supprimer() sans lever d'exception).

D'un autre coté, cette solution est très simple à mettre en oeuvre et sa consommation en ressources supplémentaires est négligeable; elle ne requiert pas, par exemple, de dupliquer la Collection que les clients souhaitent parcourir. Dans le cas où l'on souhaite maximiser la robustesse ou faire varier le mode de parcours d'un client à travers le graphe d'objets métier, notre second Pattern est plus adapté: l'Itérateur. A l'image de ce que proposent la plupart des bibliothèques d'accès aux données avec la notion de Curseur, l'Itérateur est un objet dédié à un seul client à la fois et qui permet de parcourir en lecture seule une collection d'objets. Outre le fait qu'il simplifie le parcours à travers une structure de données, l'Itérateur est également un excellent outil de découplage entre la collection d'objet parcourus et celui qui l'arpente. En effet, quelle que soit l'organisation interne des objets, on utilise un Itérateur toujours de la même manière. L'Itérateur, quant à lui, doit avoir une relation privilégiée avec la collection dont il facilite la traversée. Or l'organisation des objets peut varier en fonction des types de collections (listes chaînées, listes basées sur des tableaux, arborescences triées...), donc il est nécessaire de disposer d'un type d'itérateur par type de collection parcourue.

Enfin, toujours pour masquer l'implémentation à l'utilisateur, il faut faire porter à la Collection d'objets elle-même la responsabilité de créer le bon type d'Itérateur (en fonction de sa structure de données interne). On dit que la Collection se comporte comme une Fabrique (ou qu'elle porte une méthode de fabrication).

Facade

Façade (Facade) : a pour but de cacher une conception et une interface complexe difficile à comprendre. La façade encapsule la complexité des interactions entre les objets métier participant à un workflow.




```console

GestionDesPublications

+AjouterTheme(theme:string):void

+PublierArticle(theme:string,titre:string,auteur:string,text:string,urlContenu:string):void

+PublierNews(theme:string,titre:string,auteur:string,text:string):void
```

Comme nous pouvons le constater, la Façade est très simple à utiliser puisqu'elle se charge de tout:

* La création des objets nécessaires. La Façade peut s'appuyer sur une Fabrique (éventuellement abstraite) pour déléguer la responsabilité d'instancier les objets concrets.
* L'initialisation complémentaire de ces objets. Certains objets n'offrent qu'un constructeur sommaire (ou leur fabrique n'offre qu'une méthode acceptant peu de paramètres); il s'agit donc d'affecter les propriétés qui n'ont pas pu l'être par le biais de ce constructeur.
- L'invocation de méthodes, dans un ordre compatible avec les workflows applicatifs et métier.

La Façade GestionDesPublications est une classe sans attribut. Ses instances sont donc sans état, indistincts. Dans ce cas, il serait dommage d'en créer plusieurs puisque chacune de ces instances est parfaitement identique à sa petite soeur.

Pour éviter ce gâchis, il convient d'interdire la création à tout va d'instances de GestionDesPublications. Comment? En rendant son constructeur inaccessible aux clients extérieurs (rendons-le privé par exemple). Mais de ce fait, il devient nécessaire d'offrir un autre point d'entrée à la GestionDesPublications, qui ne nécessite pas de l'instancier au préalable: seule une méthode statique peut faire l'affaire. Et donc sans surprise, cette méthode va permettre soit d'instancier la GestionDesPublications (la première fois qu'on l'invoque), soit de récupérer une référence à cette instance devenue unique (les fois suivantes).

Cette gymnastique de l'esprit, qui part du besoin de partager des informations ou d'optimiser l'occupation mémoire par l'interdiction de créer plusieurs instances, et qui aboutit à une classe à l'instance unique accessible par le biais d'une méthode statique porte un nom: le Design Pattern Singleton. Dans la phrase précédente, on comprend comment les Patterns permettent d'élever le niveau du langage et de limiter les ambiguïtés entre interlocuteurs avertis.


== Amélioration de la traçabilité ==

Introduire une Façade a certes simplifié l'accès aux fonctionnalités essentielles de notre système, mais cela n'a pas amélioré la traçabilité ni la gestion de l'historique des interactions entre l'utilisateur final et le système. Mais nous sommes près du but: puisque nous ne pouvons cataloguer que des objets, et non des invocations de méthodes, il faudrait simplement que chaque sollicitation de la classe GestionDesPublications soit réifiée en objet, c'est-à-dire qu'il nous faudrait instancier un objet pour rendre compte de chaque invocation des méthodes AjouterThème(), PublierArticle() et PublierNews().

Afin de banaliser la gestion de toutes ces interactions, nous allons nous efforcer de respecter le

même contrat dans chacune de ces nouvelles classes de conception. Typiquement, elles offriront toutes

une méthode Execute() qui permettra de déclencher l'invocation du service associé. Seules varieront

les méthodes qui permettront de fournir les informations spécifiques nécessaires à l'exécution de

chaque commande (les "Setters"), ainsi que les méthodes de récupération des résultats éventuels (les

"Getters"). Cette manière de faire s'appelle le Pattern Commande:



### Design Pattern Façade

```console

GestionDesPublications

+AjouterTheme(theme:string):void
+PublierArticle(theme:string,titre:string,auteur:string,text:string,urlContenu:string):void
+PublierNews(theme:string,titre:string,auteur:string,text:string):void
```

Comme nous pouvons le constater, la Façade est très simple à utiliser puisqu'elle se charge de tout:

* La création des objets nécessaires. La Façade peut s'appuyer sur une Fabrique (éventuellement abstraite) pour déléguer la responsabilité d'instancier les objets concrets.
* L'initialisation complémentaire de ces objets. Certains objets n'offrent qu'un constructeur sommaire (ou leur fabrique n'offre qu'une méthode acceptant peu de paramètres); il s'agit donc d'affecter les propriétés qui n'ont pas pu l'être par le biais de ce constructeur.
- L'invocation de méthodes, dans un ordre compatible avec les workflows applicatifs et métier.

La Façade GestionDesPublications est une classe sans attribut. Ses instances sont donc sans état, indistincts. Dans ce cas, il serait dommage d'en créer plusieurs puisque chacune de ces instances est parfaitement identique à sa petite soeur.

Pour éviter ce gâchis, il convient d'interdire la création à tout va d'instances de GestionDesPublications. Comment? En rendant son constructeur inaccessible aux clients extérieurs (rendons-le privé par exemple). Mais de ce fait, il devient nécessaire d'offrir un autre point d'entrée à la GestionDesPublications, qui ne nécessite pas de l'instancier au préalable: seule une méthode statique peut faire l'affaire. Et donc sans surprise, cette méthode va permettre soit d'instancier la GestionDesPublications (la première fois qu'on l'invoque), soit de récupérer une référence à cette instance devenue unique (les fois suivantes).

Cette gymnastique de l'esprit, qui part du besoin de partager des informations ou d'optimiser l'occupation mémoire par l'interdiction de créer plusieurs instances, et qui aboutit à une classe à l'instance unique accessible par le biais d'une méthode statique porte un nom: le Design Pattern Singleton. Dans la phrase précédente, on comprend comment les Patterns permettent d'élever le niveau du langage et de limiter les ambiguïtés entre interlocuteurs avertis.

== Amélioration de la traçabilité ==

Introduire une Façade a certes simplifié l'accès aux fonctionnalités essentielles de notre système, mais cela n'a pas amélioré la traçabilité ni la gestion de l'historique des interactions entre l'utilisateur final et le système. Mais nous sommes près du but: puisque nous ne pouvons cataloguer que des objets, et non des invocations de méthodes, il faudrait simplement que chaque sollicitation de la classe GestionDesPublications soit réifiée en objet, c'est-à-dire qu'il nous faudrait instancier un objet pour rendre compte de chaque invocation des méthodes AjouterThème(), PublierArticle() et PublierNews().

Afin de banaliser la gestion de toutes ces interactions, nous allons nous efforcer de respecter le même contrat dans chacune de ces nouvelles classes de conception. Typiquement, elles offriront toutes une méthode Execute() qui permettra de déclencher l'invocation du service associé. Seules varieront les méthodes qui permettront de fournir les informations spécifiques nécessaires à l'exécution de chaque commande (les "Setters"), ainsi que les méthodes de récupération des résultats éventuels (les "Getters"). Cette manière de faire s'appelle le Pattern Commande.

Poids-mouche

  • Poids-mouche ou poids-plume (Flyweight)

  • Proxy (Proxy) : Un proxy est une classe se substituant à une autre classe. Par convention et simplicité, le proxy implémente la même interface que la classe à laquelle il se substitue. L'utilisation de ce proxy ajoute une indirection à l'utilisation de la classe à substituer.


Commentaires

Posts les plus consultés de ce blog

Base de Données Sybase IQ

Sécurité des Applications

Principes de la Programmation Orientée Objet