0% ont trouvé ce document utile (0 vote)
86 vues24 pages

Cpatterns

Transféré par

laurent capoeira
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
86 vues24 pages

Cpatterns

Transféré par

laurent capoeira
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Université Montpellier

UFR des Sciences - Département Informatique


Master Informatique 1

Ingénierie Logicielle - Concepts et Outils de la modélisation et du développement de logiciel


par et pour la réutilisation.
Partie No 2 : Réutilisation des Conceptions et des Architectures Logicielles

Notes de cours
Christophe Dony

1 Introduction

Idée
— Architecture - génie civil (Christopher Alexander) : Each pattern describes a problem which occurs over and over
again in our environment, and then describes the core of the solution to that problem, in such a way that you can
use this solution a million times over, without ever doing it the same way twice.
— Informatique - génie logiciel
livre de base : [GHJV 94] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides Design Patterns : Elements
of Reusable Object-Oriented Software Addison Wesley, 1994.
— Le livre en ligne : [Link]
— les schémas du livre en ligne : [Link]

Définition :
— Un Schéma de conception nomme, décrit, explique et permet d’évaluer une conception d’un système exten-
sible et réutilisable digne d’intérêt pour un problème récurrent.
Intérêts

— vocabulaire commun (communication, documentation et maintenance, éducation)


— gain de temps (explication, réutilisation)

Les éléments d’un schéma


— Un problème (récurrent)
— Un nom
— Une solution intéressante : les éléments (diagrammes UML, code) qui traitent le problème ; leurs relations, leurs
rôles et leurs collaborations.
— Des analyses de la solution et ses variantes : avantages et inconvénients de l’utilisation du schéma, considérations
sur l’implantation, exemples de code, utilisations connues, schémas de conception connexes, etc.

Paramétrage, Variabilité
Les schémas offrent des solutions génériques paramétrées selon les schémas par spécialisation et composition présentés
dans la première partie de ce cours.

1
Ce qui varie Schéma
Algorithmes Strategy, Visitor
Actions Command
Implementations Bridge
Réponse aux changements Observer
Interaction entre objects Mediator
Création des objets Factory, Prototype
Création des données Builder
Traversal algorithm Visitor, Iterator
Object interfaces Adapter
Object behaviour Decorator, State

Figure (1) – Classification extraite de [GHJV 94]

1.1 Généralisation et Extension : l’ingénierie des “design patterns”


— Succès de l’idée : des schémas sur tous les sujets
Object-Oriented Reengineering Patterns Par Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz, 2002, Morgan
Kaufmann, 282 pages, ISBN 1558606394
[Link]
— Foutse Khomh. Patterns and quality of Object-oriented Software Systems. 2010.
— Guéhéneuc Yann-Gaël, Khashayar Khosravi. A Quality Model for Design Patterns. 2004.
[Link]
— Carl G. Davis Jagdish Bansiya. A Hierarchical Model for Object-Oriented Design Quality Assessment. 2002.
— Yann-Gaël Guéhéneuc Hervé Albin-Amiot, Pierre Cointe. Un méta-modèle pour coupler application et détection des
design patterns. 2002.
— Tu Peng Jing Dong, Yajing Zhao. Architecture and Design Pattern Discovery Techniques. 2007.
— Jakubik Jaroslav. Extension for Design Pattern Identification Using Similarity Scoring Algorithm. 2009.
— Marcel Birkner. Objected-oriented design pattern detection using static and dynamic analysis in java software. 2007.

1.2 Classification des schémas


— Schémas créateurs : Décrire des solutions pour la création d’objets,
— Schémas structuraux : Décrire des solutions d’organisation structurelles,
— Schémas comportementaux : Décrire diverses formes de collaboration entre objets.

2 Un exemple de schéma créateur : “Singleton”


Problème : Faire en sorte qu’une classe ne puisse avoir qu’une seule instance (ou par extension, un nombre donné
d’instances).
Exemple : Les classes True et False si les booléens sont des objets ( Smalltalk, Javascript, Python, ...) .

Solution “tout langage”

1 public class Singleton {


2 private static Singleton INSTANCE = null;

4 /∗∗ La présence d’un constructeur privé (ou protected) supprime


5 ∗ le constructeur public par défaut, et empêche un ‘‘new’’ externe.∗/

2
6 private Singleton() {}

8 /∗∗ ’’synchronized’’ sur la méthode de création


9 ∗ empêche toute instanciation simultanée par différents threads.
10 ∗ Rend l’unique instance de la classe,
11 ∗ sauvegardée dans un attribut de classe (statique) ∗/

13 public synchronized static Singleton getInstance() {


14 if (INSTANCE == null)
15 INSTANCE = new Singleton();
16 return INSTANCE;}
17 }

Listing (1) – Une classe Singleton en Java.

3
Discussions
1. Commentaire : empêcher les instantiations par les clients : visibilité du constructeur,

1 private Singleton() {}

2. Commentaire : Empêcher les copies


— (Si C++) - déclarer sans le définir le constructeur par copie)

1 Singleton (const Singleton&) ;

— (Si Java) - ne pas implanter l’interface Cloneable.


3. Alternative : (si C++) surcharger new ,

1 class iSingleton{
2 static iSingleton ∗INSTANCE;
3 public:
4 iSingleton() {
5 INSTANCE=this;
6 }

8 void ∗operator new(size_t s) {


9 if(INSTANCE != NULL)
10 return INSTANCE;
11 else
12 return malloc(s);
13 }

Listing (2) – Surcharge de l’opérateur new en C++.

4. Alternative : si ∃ métaclasse Class et si new est une méthode ( exemples : Pharo, Smalltalk, Python, Ruby, ...),
Redéfinir new sur chaque classe de classe Singleton :

1 class Singleton class


2 new
3 (INSTANCE isNil) ifTrue: [INSTANCE := super new].
4 return (INSTANCE)

Listing (3) – Singleton,via une redéfinition de la méthode new. INSTANCE est toujours un attribut de classe
(static)

5. Alternative : si le langage permet de définir de nouvelles métaclasses (exemple : Common-Lisp),


Définir une méta-classe Singleton-Class, instance et une sous-classe de Standard-class.

1 (defclass singleton-class (standard-class)


2 ((UNIQUE-INSTANCE :accessor GET-INSTANCE :initform nil))
3 (:metaclass standard-class))

Listing (4) – Singleton, via une nouvelle méta-classe Singleton-class, la classe des classes qui ne peuvent
avoir qu’une seule instance. Elle possède un attribut de méta-classe Unique-Instance. Version Common-Lisp

1 (defmethod make-instance ((newC singleton-class) &rest args)


2 (or (GET-INSTANCE newC)
3 (let ((newI (call-next-method)))
4 (setf (get-instance newC) newI)
5 newI)))

4
Listing (5) – Spécialisation de l’instantiation sur singleton-class, (call-next-method’) réalise l’appel de la
méthode masquée par la redéfinition (envoi de message à super).

6. Exemple : Utilisation du schéma Singleton pour les classes True et False, possible discussion sur l’implantation
des structures de contrôle dans le monde objet et l’intérêt des fermetures lexicales.

3 Un exemple de schéma structurel : “Decorateur” ou “Wrapper”

3.1 Problème
Ajouter dynamiquement une fonctionnalité à un objet (individuel), sans modifier ni sa classe, ni donc les autres instances
de sa classe.
Application possible : Ajouter des décorations (“barre de scroll”, “bordure”) à un objet graphique (Figure 2).

3.2 Exemple Type : décoration d’une “textView”

TextView ScrollDecorateur BordureDecorateur

draw() scrollPosition borderColor

drawScrollBar() drawBorder()
scrollTo()

SrollBorduredTextView

{[Link]();
draw() [Link]();
[Link]();}

Figure (2) – Décoration d’une textview, solution universelle avec héritage multiple. Limitations : a) statique
b) autant de sous-classes que de combinaisons potentielles de décorations.

5
ComposantVisuel

#draw()

composant
TextView Decorateur

draw() draw() {[Link]();}

ScrollDecorateur BordureDecorateur

scrollPosition draw() {[Link]();


[Link]();}
draw() drawBorder()
scrollTo()

Figure (3) – Décoration d’une Textview : solution avec le schéma “Decorateur”

Un BorderDecorateur

composant Un ScrollDecorateur

composant une TextView

Figure (4) – Objets représentant une Textview décorée selon le schéma “Decorateur”.

6
3.3 Principe Général de la solution (figure 5)

est−décoré−par
Composant
1
operation()

décoré
ComposantConcret Decorateur
1
operation() operation() {décoré.operation();}

DecorateurConcretA DecorateurConcretB

attributs operation() {[Link]();


[Link]();}
operation() decoration()

Figure (5) – Composant : objet métier quelconque à décorer, exemple : textView. Décorateur : objet
décorant (donc ajoutant des fonctionnalités) à un objet métier, exemple : scrollDecorator).

3.4 Une mise en oeuvre concrète


Une implantation de “décorateur” sur un exemple jouet de composants graphiques (classe Composant).

1 public abstract class Composant {


2 abstract public void draw();
3 }

5 public class VueTexte extends Composant{


6 public void draw() { ... } //affiche un texte
7 }

1 public class Decorateur extends Composant {


2 Composant décoré;

4 public Decorateur(Composant c){


5 //injection de dépendance
6 //affectation polymorphique
7 décoré = c;}

9 public void draw(){


10 //redirection de message
11 décoré.draw();} //’’dispatch 2, sélection selon le composant décoré’’
12 }

7
1 public class BorderDecorator extends Decorateur {
2 // ajoute une bordure à un composant graphique

4 float largeur; //largeur de la bordure

6 public BorderDecorator(Composant c, float l) {


7 super(c);
8 largeur = l; }

10 public void draw(){


11 [Link]();
12 [Link](); }

14 public void drawBorder() {


15 // dessin de la bordure
16 ... }
17 }

1 class Application {
2 public static void main(String[] args){
3 Composant c = new VueTexte(’’Hello’’);
4 //ajoûter une décoration
5 c = new BorderDecorator(c, 2.5);
6 [Link](); //dispatch 1, sélection selon la décoration

Application - Les streams en Java ([Link]

InputStream is an abstract class. Most concrete implementations like BufferedInputStream, GzipInputStream,


ObjectInputStream, etc. have a constructor that takes an instance of the same abstract class. That’s the recog-
nition key of the decorator pattern (this also applies to constructors taking an instance of the same interface).
... Let’s say that we have a bunch of serialized Java objects in a Gzipped file and that we want to read them
quickly.

1 //First open an inputstream of it:


2 FileInputStream fis = new FileInputStream("/[Link]");

4 //We want speed, so let’s buffer it in memory:


5 BufferedInputStream bis = new BufferedInputStream(fis);

7 //The file is gzipped, so we need to ungzip it:


8 GzipInputStream gis = new GzipInputStream(bis);

10 //We need to unserialize those Java objects:


11 ObjectInputStream ois = new ObjectInputStream(gis);

13 //Now we can finally use it:


14 SomeObject someObject = (SomeObject) [Link]();

3.5 Discussions
1. Peut-on réaliser un décorateur en Javascript avec le lien proto : oui.
L’héritage entre objets, via le lien proto ouvre de nouvelles possibilités de décoration ;

8
1 var User = function (name,number) {
2 [Link] = name;
3 [Link] = number;
4 [Link] = function () {
5 return("User: " + [Link]); }; }

7 var user = new User("Kelly",22);


8 [Link](); // User: Kelly

10 var deco = new Object();


11 deco.__proto__ = user;
12 [Link]=function(){return"decorated " + (this.__proto__.say()) + [Link];}
13 [Link](); // decorated User: Kelly22

15 var decodeco = new Object();


16 decodeco.__proto__ = deco;
17 [Link](); //decorated decorated User: Kelly2222

Quelles sont les propriétés de cet héritage ?


Héritage de valeurs pour les méthodes et pour les attributs. En lecture. Et en écriture ?
1 var personne1 = {
2 prenom:"Jean",
3 nom:"Dupont",
4 getPrenom:function(){return [Link]},
5 getNom:function(){return [Link]},
6 setNom:function(n){[Link]=n},
7 };

9 var personne2 = {
10 __proto__: personne1, //lien proto induisant un héritage entre objets
11 prenom: "Paul",
12 };

14 [Link](); // Jean
15 [Link](); // Paul ... héritage de getPrenom() et liaison dynamique
16 [Link]("Martin");
17 [Link](); // Martin
18 [Link](); // ????

2. Problème : nécessité pour un décorateur d’hériter de la classe abstraite Composant et donc de redéfinir toutes les
méthodes publiques pour réaliser une redirection de message.
3. Problème : poids des objets : il est recommandé de ne pas définir (trop) d’attributs dans la classe abstraite composant
afin que les décorateurs restent des objets “légers”.
Une solution à ce problème est de modifier le pattern en remplaçant la classe abstraite Composant par une interface,
et donc le lien sous-classe-de entre Décorateur et Composant par un lien implémente.
4. Problème : incompatibilité potentielle de différentes décorations.
5. Problème : L’ordre d’ajout des décorations est signifiant.
6. commentaire : un décorateur peut être vu comme un composite avec un seul composant, mais le décorateur a des
fonctionnalités propres que n’a pas son composant ...

3.6 Double liaison (double dispatch) : appeler la bonne méthode en présence de multiples
affectations polymorphiques
separation of responsibility for type-specific logic
Le schéma “décorateur” permet que les décorations soient de différentes sortes mais également que les objets décorés
puissent l’être, en fournissant différentes implantations de la méthode draw(), si l’on garde l’exemple utilisé en section 3.

9
L’envoi de message réalise une sélection de méthode selon le type dynamique du receveur, mais pas selon le type dynamique
des autres paramètres (sauf en Common-Lisp-Object-System).
Le schéma “double-dispatch” stipule qu’il est nécessaire dans une application d’avoir un envoi de message pour chaque
type statique utilisé.
Exemple : différents décorateurs sur différentes sortes de textView.
separation of responsibility for type-specific logic

4 Un exemple de schéma structurel : Adapteur

Problème
Intégrer dans une application client une instance d’une classe existante dont l’interface ne correspond pas à la façon dont
le client doit l’utiliser.
Fait intervenir les Participants suivants :
Cible : objet définissant le protocole commun à tous les objets manipulés par le client, (dans l’exemple : shape)
Client : objet utilisant les cibles (l’éditeur de dessins)
Adapté : l’objet que l’on souhaite intégrer à l’application
Adapteur : objet réalisant l’intégration.

4.1 Application Typique


Intégrer dans une application en cours de réalisation une instance d’une classe définie par ailleurs (cf. fig. 6).
Soit à réaliser un éditeur de dessins (le client) utilisant des objets graphiques (les cibles) qui peuvent être des lignes,
cercles, quadrilatères mais aussi des textes.
On pose comme contrainte de réutiliser une instance (l’objet à adapter) d’une classe textview définie par ailleurs.

uses
DrawingEditor DrawingShapes TextView

abstract fillarea() boundingBox()

... complex code ...

Circle square TextAdapter

fillArea() fillArea() fillArea() return([Link]()

Figure (6) – Exemple d’adaptation par spécialisation

10
4.2 Principe général de la solution : figures 7 et 8

Client Cible Adapté

requete() requeteSpecifique()

Adapteur

Requete() return([Link]())

Figure (7) – Adapteur réalisé par spécialisation

Client Cible Adapté

requete() requeteSpecifique()

adapté
Adapteur

Requete() return(adapté.requeteSpecifique())

Figure (8) – Adaptateur réalisé par composition

4.3 Discussion
1. Application à la connexion non anticipée de composants. Voir cours “[Link]”.
Exemple, intégrer un compteur (unCompteur instance de Compteur définie dans une bibliothèque) dans une appli-
cation graphique existante.
Problème : la classe Compteur n’implante pas l’interface MouseListener.

1 [Link](new [Link]() {
2 public void mouseClicked([Link] evt) {
3 [Link]();
4 }
5 });

Listing (6) – Solution Java : adaptation d’un objet externe via une classe anonyme dotée d’une méthode
conforme au client,réalisant la redirection de message

11
2. Mise en évidence du problème posé par la redirection de message ici liée à l’utilisation de la composition en
paliatif de l’héritage multiple :
— Nécessité de redéfinir sur l’adapteur toutes les méthodes publiques de l’adapté.
— Redirection de message : perte du receveur initial (cf. fig. 9). initial du message est perdu, ceci rend certains
schémas de réutilisation difficiles à appliquer.

4.4 Discussion sur redirection et perte du receveur

uses
DrawingEditor DrawingShapes TextView

abstract fillarea() boundingBox()

...complex code...; [Link]();

Circle square TextAdapter

fillArea() fillArea() fillArea()

{return(new TextView().boundingBox());
1.

Figure (9) – Adaptation par composition dans l’exemple de l’éditeur de dessin ; receveur initial perdu dans la
méthode boundingBox(). Ceci rend en l’état impossible la prise en compte d’une spécialisation dans l’application
de la méthode plugin de la classe adaptée TextView. La figure 10 propose un schéma global de solution à ce
problème.

uses
DrawingEditor DrawingShapes TextView

abstract fillarea() boundingBox()

...complex code...; [Link]();

Circle square TextAdapter

fillArea() fillArea() fillArea()

MyTextView

plugin()

{return(new MytextView().boundingBox());
2.

Figure (10) – Adaptation par composition et solution partielle au problème de perte du receveur initial.

3. Généralisation : Adapter, Proxy, State

12
La “redirection de message” et la “perte du receveur initial” associée apparaissent dans de nombreux schémas
utilisant la composition ...
dont ... dont Adapter, Proxy, State .

Proxy versus Adapter :


Un Adapter adapte l’interface (de l’adapté) , en préservant le comportement.
Un Proxy modifie (ou simplifie) le comportement (de l’adapté) tout en préservant l’interface. (exemple, le talon côté
client en programmation distribuée).
4. Etude (pour ne pas confondre avec le précédent) de la perte du receveur initial en héritage+liaison statique.
On teste ici l’héritage en liaison statique nommé (“embarquement de type” (embedded type du langage GO. On
obtiendrait le même comportement avec des méthodes non virtuelles en C++.

1 package main

3 type Widget struct {


4 X, Y int
5 }
6 type Label struct {
7 Widget // Embarquement de type (sorte d’héritage matérialisé par un attribut sans
nom)
8 Text string // Composition
9 }
10 func (l Label) KindOf() string{
11 return "Label"}

13 func (l Label) Paint() {


14 [Link]("un %q : %q %d %d \n", [Link](), [Link], l.X, l.Y)}

Listing (7) – Go - Embarquement de type.


(Adapted from
http ://[Link]/open-source/go-introduction-how-go-handles-objects/240005949)

Figure (11)

13
1 type Button struct {
2 Label // Embarquement (héritage matérialisé par un attribut sans nom)
3 }
4 func (b Button) Click() {
5 [Link]("%p:[Link]\n", &b) }
6 func (b Button) KindOf() string {
7 return "Button" }

9 func main() {
10 label := Label{Widget{10, 10}, "Joe"}
11 [Link]() //un ”Label” : ”Joe” 10 10

13 button1 := Button{Label{Widget{10, 70}, "Bill"}}


14 [Link]() //ok, fonctionne

16 [Link]() //un ”Label” : ”Bill” 10 70


17 //... appel de la méthode ‘‘héritée’’ Paint, mais en liaison statique
18 //... donc appel de KindOf de Label et pas de kindOf de Button
19 // ... équivalent à perte du receveur initial

21 [Link]() //un ”Label” : ”Bill” 10 70


22 //... la preuve, on obtient le même résultat en envoyant le message au label

Listing (8) – Point clé : appeler “[Link]” donne le même résultat que “[Link]()” ... le
receveur passé à la méthode Paint est le Label, pas le Button

5 Bridge : Séparation des interfaces et des implantations

5.1 Problème et Principe


Problème : Découpler une hiérarchie de concept des hiérarchies réalisant ses différentes implantations.
Principe (cf. fig. 12) : Bridge utilise l’adaptation par composition pour séparer une hiérarchie de concepts de différentes
hiérarchies représentant différentes implantations de ces concepts.

imp
Window WindowImp

drawText() drawText()
drawRect() drawLine()

{repeat 4
[Link](dir); dir += 90;}}

XWindowImp MacWindowImp
IconWindow TranscientWindow
drawText() drawText()
drawBorder() drawCloseBox() drawLine() drawLine()

{[Link](size/10); {[Link]();} {[Link]();}


[Link](size/10) {[Link]();}

Figure (12) – Exemple d’application du schéma “Bridge”

5.2 Discussion
— Implantation : Qui décide des types de classes d’implantation créer ? Constructeur avec argument (lien à faire avec
l’utilisation d’un framework paramétré par composition). Test dynamique selon critère. Délégation du choix à un
objet externe, lien avec une fabrique (schéma Factory).

14
— Bridge évite la création de hiérarchies multi-critères mélant les classes conceptuelles et les classes d’implémentation.
— Les concepts et les implantations sont extensibles par spécialisation de façon indépendantes.
— Les clients sont indépendants de l’implantation (Il est possible de changer une implantation (recompilation) sans
que les clients n’en soient affectés).
— L’idée liée à celle d’interface (à la Java), alternative autorisant la définition de méthodes dans la hiérarchies des
concept.

6 Schéma comportemental : “State”

6.1 Problème et Principe


Le schéma “State” propose une architecture permettant à un objet de changer de comportement quand son état interne
change (cf. fig. 13).

etatCourant
Object Etat

requete() requete()

{return([Link]());} EtatConcretA EtatConcretB

requete() requete()

{...;
[Link](...);

Figure (13) – Pricipe général du schéma “State”

15
6.2 Exemple d’application : implantation d’une calculatrice

enter(String) enter(String)

Nombre 1 Opération Nombre2


acc1 : v1 acc1 : v1 acc1 : v1
op : null op : op op : op
acc2 : 0 acc2 : 0 acc2 : v2

enter(String)

Figure (14) – Les différents états et transitions d’une “calculatrice” basique

Gestion des états

1 public class Calculette {


2 protected EtatCalculette etatCourant;
3 protected EtatCalculette[] etats = new EtatCalculette[3];
4 double accumulateur;
5 String operateur;

7 public Calculette(){
8 etats[0] = new ENombre1(this);
9 etats[1] = new EOperateur(this);
10 etats[2] = new ENombre2(this);
11 etatCourant = etats[0];
12 accumulateur = 0; }
13 ...
14 // accesseurs lecture/écriture pour ‘‘accumulateur’’ et pour ‘‘operateur’’
15 ...
16 //obtention du résultat
17 public double getResult() { return accumulateur; }

Distribution des calculs

1 public class Calculette {


2 ....

4 public void enter(String s) throws CalculetteException{


5 //toute requète est redirigée vers l’état courant
6 //qui, dans cette implantation, décide quel est l’état suivant. Ce n’est pas une règle générale.
7 etatCourant = etats[ [Link](s) − 1]; }

16
Les états sont invisibles aux clients

1 public static void main(String[] args){


2 Calculette c = new Calculette();
3 [Link]("123"); //etat 1 : stocke le nombre 123 dans accumulateur
4 [Link]("plus"); //etat 2 : stocke l’operation a effectuer dans un registre
5 [Link]("234"); //etat 3 : stocke le résultat de l’opération dans accumulateur
6 [Link]([Link]());}

Classe abtraite de factorisation

1 abstract class EtatCalculette {


2 static protected enum operations {plus, moins, mult, div};
3 abstract int enter(String s) throws CalculetteException;
4 Calculette calc;

6 EtatCalculette(Calculette c){ calc = c; }


7 }

Calculette dans état initial, dans l’attente de l’entrée d’un premier opérande

1 public class ENombre1 extends EtatCalculette{

3 ENombre1(Calculette c) { super(c); }

5 public int enter(String s) throws CalculetteException {


6 try{[Link]([Link](s));}
7 catch (NumberFormatException e)
8 {throw new CalculetteNumberException(s);}
9 //l’état suivant est le 2 (entrée opérateur)
10 return(2);} }

Calculette dans l’attente de saisie de l’opération à effectuer

(une gestion des exception plus fine serait nécessaire) :

1 public class EOperateur extends EtatCalculette{


2 EOperateur(Calculette c){ super(c); }
3 public int enter(String s) throws CalculetteException {
4 [Link](s);
5 return(3);} }

Calculette dans l’attente de saisie d’un second opérande

L’application de l’opération aux opérandes peut y être réalisée :

1 public class ENombre2 extends EtatCalculette {


2 ENombre2(Calculette c){super(c);}

4 int enter(String s) throws CalculetteException {

17
5 float temp = 0;
6 try {temp = [Link](s);}
7 catch (NumberFormatException e) {
8 throw new CalculetteNumberException(s);}

10 switch ([Link]([Link]())) {
11 case plus: [Link]([Link]() + temp); break;
12 case mult: [Link]([Link]() ∗ temp); break;
13 default:
14 throw new CalculetteUnknownOperator([Link]());}
15 return (1);}}

6.3 Discussion
1. Implémentation : comment représenter l’état courant ?
2. Ce schéma rend explicite dans le code les changements d’état des objets : applications pour la sécurité (contrôle du
bon état interne des objets) et la réutilisation (exercice : passer d’une calculette infixée à une postfixée).
3. Evolution des Langages : constructions prenant en compte la catégorisation selon l’état (exemple : la définition par
sélection de Lore).

1 [Majeur isa select


2 from Person //ensemble réel de référence
3 such−that [[self age] >= 18] //prédicat de sélection
4 with (slot droit−vote init−value true)]

4. Evolution des Langages : constructions pour le changement de classe.


— Smalltalk : primitive Class>>adoptInstance: changes the class of an object, and thus its behavior.
1 Object subclass: #Personne
2 instanceVariableNames: ’nom age’

4 Personne subclass: #Majeur


5 instanceVariableNames: ’’

7 Personne subclass: #Mineur


8 instanceVariableNames: ’’

10 !Personne methodsFor: ’accessing’!


11 age
12 ˆage ! !

14 vieillir
15 age := age + 1.
16 (age = 18) ifTrue: [ Majeur adoptInstance: self ]! !

Listing (9) – Application de AdoptInstance

— Smalltalk : programmer la calculette avec Class>>adoptInstance:

18
Figure (15) – Une implantation du schéma “State”, utilisant la possibilité pour une instance de changer
dynamiquement de classe.

— Apparté : application du changement dynamique de classe ; le cas de la mise au point de programmes à exécution
continue (Dynamic Software Update)

Figure (16) – Adaptation non-anticipée de comportement : application au déverminage de programmes en


cours d’exécution - thèse Steven Costiou.

— Objective-C (implantation de MacOS et de IOS) : primitive object setClass


5. Evolution des Langages : utiliser la délégation pour représenter des points de vues.
Passer d’un état à un autre, c’est aussi changer de point de vue : comment représenter différents points de vues sur
le même objet, de façon à ce que le même message soit traité différemment selon l’état courant de l’objet.

19
Figure (17) – Partage de valeur entre objets en programmation par prototypes, application aux points de vues.
- Daniel Bardou and Christophe Dony. Split Objects : a Disciplined Use of Delegation within Objects. October
1996.

7 Le schéma comportemental : “Observateur”

7.1 Problème
Faire qu’un objet devienne un observateur d’un autre afin qu’à chaque fois que l’observé est modifié, l’observateur soit
prévenu.
Exemple d’application :
- Abonnements, toutes formes de “publish/subscribe”,
- connexion non anticipée de composants
- IHM (MVC).

20
7.2 Principe général de la solution (Figure 18)

Figure (18) – Le schéma Observer permet d’établir une collaboration entre un observateur et un observé (ou
“sujet” selon ce diagramme).

7.3 Discussion
— MVC, Event-Based, ...
— Analyser la conception du découplage : [Link]()
— Mémorisation des écouteurs et “garbage collector”.
— Les limites de la séparation des préoccupations (separation of concerns) selon le schema Observateur : comment
enlever les [Link]() du code métier.
— Observer : Classe abstraite versus interface.

7.4 Une mise en oeuvre typique avec l’exemple du framework MVC


Historique :

— Le schéma MVC (article 1 [Krasner, Pope 1977]), un mécanisme permettant :


— découpler les traitements métier (le modèle) de l’affichage (la vue)
— découpler le contrôle (souris, clavier) de l’affichage
— avoir plusieurs vues (simultanées) sur un même objet ou changer facilement de vue.
— 1995 : schéma “observateur” généralisation de l’idée de séparation entre observeur et observé.
— 1995 le schéma MVC est un “framework” proposant une architecture logicielle et un ensemble de paramétres (pa-
ramétrage par spécialisation) pour la réutilisation.
Une implantation ainsi qu’une application qui en dérive sont données dans les sections suivantes.

7.5 Architecture
Trois hiérarchies de classes définissent les modèles, les vues et les contrôleurs.
1. Krasner Pope tutorial on MVC : [Link]

21
– Tout modèle (objet métier) est défini par une sous-classe de la classe Model. Un modèle peut avoir des observateurs (en
premier lieu des vues).
– Tout contrôleur est définie par une sous-classe de Controller, chaque contrôleur connait son modèle (et si on le souhaite
sa vue), le contrôleur observe l’utilisateur.
– Toute vue est définie par une sous-classe de View. Chaque vue connait son contrôleur et son modèle.

7.6 Modèle d’interaction (figure 19)

Utilisateur Souris Modele


Clique Emet Evènement Exécute methode métier

Demande action Demande MAJ


Demande état

Controleur Vue1
Capte Evenement Affiche état modèle
demande MAJ

Figure (19) – MVC : Interactions entre modèles, vues et contrôleurs

7.7 Implantation (squelette) inspirée de l’implantation originelle (Smalltalk-80)


Parce que le dessin du pattern ne dit pas tout, ceci est une tentative de décrire le Framework, ses mécanismes, et une
application (exécutable) de la façon la plus succinte possible.

7.7.1 Gérer les dépendances “observateur-observé”

Classe du framework

1 public class MV−Association{


2 //utilise un dictionnaire pour stocker les couples modèle−vue
3 static Dictionary MVDictionary = new Hashtable();

5 //permet d’associer un modèle à une vue


6 public static void add(Model m, View v) { ... }

8 // rend la collection de vues associées à un modèle


9 public static Collection<View> getViews(Model m) { ... }

7.7.2 Les modèles

Classe du framework

1 public class Model{

3 public void changed(Object how){


4 Iterator i = MV−[Link](this).iterator();
5 while ([Link]())
6 ([Link]()).update(how);}
7 }

22
Classe d’une l’application

1 public class Compteur extends Model{


2 protected int valeur = 0;

4 protected changerValeur(i){
5 valeur = valeur + i;
6 [Link]("valeur");}

8 public int getValeur(){return valeur;}


9 public void incrémenter(){[Link](1);}
10 public void décrémenter(){[Link](−1);}

7.7.3 Les vues

Classe du framework

1 public class View{


2 Controller cont;
3 Model model;

5 public View(Model m, Controller c){


6 model = m;
7 cont = c;
8 [Link](this, m);

10 public abstract void update (Object how);


11 public void open(){...}
12 public void redisplay(){...}
13 }

Classe d’une application

1 public class CompteurView extends View{


2 ...
3 private JLabel l;
4 ...

6 public void update(Object how){


7 l = new JLabel([Link]([Link]()), [Link]);
8 [Link]();

7.7.4 Les contrôleurs

Classe du framework

1 public abstract class Controller implements ActionListener{


2 Model m;

4 public Controller(Model m){


5 model = m;
6 }
7 }

23
Classe d’une application

1 public class CompteurController


2 extends controller
3 implements ActionListerner{

5 public void actionPerformed(ActionEvent e) {


6 if ([Link]() == "incr") {[Link]́menter();}
7 if ([Link]() == "decr") {[Link]́menter();}
8 }
9 }

7.7.5 Lancement de l’application

1 Compteur m = new Compteur();


2 CompteurView v = new CompteurView(m, new CompteurController(m));
3 [Link]();

8 Généralisation, Spécialisation
L’utilisation des schémas de conception s’est généralisée, comme en témoigne par exemple la bibliothèque en ligne “Portland
Pattern Repository” ().
Les langages de schémas (ensemble de schémas traitant d’une question globale et faisant référence les uns aux autres) se
sont spécialisés :
— reengineering patterns
— Architectural pattern in computer science
— Interaction patterns
— .̇.
et encore plus spécifiquement :
— exception handling patterns
— user interface patterns,
— .̇.

24

Vous aimerez peut-être aussi