Présentation des « design patterns »
Programmation objet
Cours n°13
Un « design pattern » (en français patron de conception) est une solution canonique à un problème précis.
Il n'est pas lié à un langage particulier, néanmoins il nécessite le paradigme de programmation objet.
Souvent le « pattern » est décrit sous la forme d'un diagramme UML.
L'avantage d'utiliser une solution canonique à un problème courant est évident :
● La solution est éprouvée
● Le gain de temps en conception est important
● L'implémentation peut être réutilisée facilement
● Le code gagne en lisibilité puisque le « pattern » est connu
Le singleton
Problème : s'assurer de l'unicité d'une instance d'une classe particulière
Utilisations : variable globale, espace partagée dans l'application, verrou, ressource Synopsis du patron :
Pour s'assurer de ne pas créer d'autres instances de l'objet, les constructeurs seront déclarés privés. Il faut donc également fournir une méthode statique pour construire et/ou accéder à l'objet. Celui-ci contiendra donc en interne un pointeur sur une instance de lui-même, privé et statique.
-singleton()
+getInstance() : singleton -pInstance : singleton
singleton
Implémentation en C++
class singleton { private:
singleton(){}
static singleton* pInstance;
public:
static singleton& getInstance() {
if(pInstance==NULL)
pInstance = new singleton();
return *pInstance;
} ~singleton(){delete pInstance;}
};
singleton* singleton::pInstance = NULL;
Utilisation du singleton
On suppose que la classe contient les méthodes f() et g()
singleton::getInstance().f();
/* ,,,,, */
singleton::getInstance().g();
Implémentation en C# ou JAVA
public class singleton
{ private static singleton pInstance;
private singleton() { }
static singleton(){ pInstance = null;}
public static singleton getInstance() {
if(pInstance==null)
pInstance = new singleton();
return pInstance;
}; }
L'adaptateur
Problème : s'assurer de la conformité à une norme (différente de celle utilisée
par une bibliothèque), ne pas dépendre d'une implémentation, permettre l'évolution Utilisation : adapter la norme d'une bibliothèque à une norme désirée, permettre l'utilisation simple de plusieurs bibliothèques aux API différentes
Synopsis du patron : une classe interface représentant l'interface désirée, une classe implémentation appelant l'API
+requete0()
«interface»
Interface1
+requete0() Classe1 CLIENT
+requete_nonconforme() CIBLE
Exemple d'implémentation en C++
Fonctions d'affichage graphique Norme désirée :
Rectangle( point, point, couleur, couleur)
Bibliothèque utilisée :
La VCL via sa classe Tcanvas Canvas->Brush->Color = couleur Canvas->Rectangle(int,int,int,int)
class iEcran { public:
virtual void Rectangle(point, point, couleur, couleur) = 0;
};
Interface :(adaptateur)
Implémentation de l'adaptateur : class AdapteVCL : public iEcran
{ private:
TCanvas *Canvas;
public:
AdapteVCL(TCanvas *C):Canvas(C){}
void Rectangle(point p1, point p2, couleur c1, couleur c2) { Canvas->Brush->Color = Tcolor(c1);
Canvas->Rectangle(p1.x, p1.y, p2.x, p2.y);
}; }
Utilisation dans la classe cliente :
iut::SharedPtr<AdapteVCL> ecran (new AdapteVCL( PaintBox1->Canvas) );
ecran->Rectangle(....);
Autre exemple d'adaptateur : pile On désire une interface en français pour une pile...
L'implémentation peut utiliser, par exemple, std::stack Voici l'interface :
template <class elt>
class IPile
{ virtual void Empile(const elt& p)=0;
virtual void Depile()=0;
virtual elt& Sommet()= 0;
virtual const elt& Sommet() const = 0;
virtual ~IPile(){}
virtual int Nombre() const = 0;
};
Méthode 1 : en utilisant l'héritage
La classe utilise un héritage d'interface (IPile) et un héritage d'implémentation (std::stack)
template <class elt>
class Pile : public IPile<elt>, private std::stack<elt>
{ public:
void Empile(const elt& p){push(p);}
void Depile(){pop();}
elt& Sommet(){return top();}
const elt& Sommet() const {return top();}
int Nombre() const {return size();}
};
Héritage d'interface Héritage d'implémentation
Méthode 2 : en utilisant un attribut
template <class elt>
class Pile : public Ipile<elt>
{ private:
std::stack<elt> the_stack;
public:
void Empile(const elt& p){the_stack.push(p);}
void Depile(){the_stack.pop();}
elt& Sommet(){return the_stack.top();}
const elt& Sommet() const {return the_stack.top();}
int Nombre() const {return the_stack.size();}
};
Les deux méthodes sont similaires et interchangeables.
L'observateur
Problème : un objet doit en prévenir un (ou plusieurs) autres d'un changement de son état
Utilisation : couplage entre classes métiers et IHM, délégation, déclenchement d'actions sur des événements précis
Synopsis du patron : une interface « sujet » qui s'abonne à un ou plusieurs
« observateurs »
+addObserver() +delObserver() +notifyAll() +notifyOne()
«interface»
Sujet
+update()
«interface»
Observateur
«classe d'implémentation»
Sujet1
«classe d'implémentation»Observateur1
«classe d'implémentation»
Observateur2 1
* le sujet contient une collection de
références sur des observateurs
L'observateur contient une référence sur le sujet auquel il est abonné
Exemple d'implémentation en C++
Mise à jour d'un affichage (cf TD dessinateur)
class observe_maj : public iObserver
{// objet chargé de faire la MAJ de l'affichage // lors d'une modif d'un objet
// il contient un pointeur sur un TpaintBox (classe VCL) // il sera utilisé par une forme qui est modifiée
private:
TPaintBox* PB;
public:
observe_maj(TPaintBox* p):PB(p){}
void maj(){ p->Invalidate(); } };
class iObserver { public:
virtual void update() = 0;
};
16 Département Informatique
Département Informatique
class iSujet { protected:
typedef iut::SharedPtr<iObserver> POBS;
typedef std::list< POBS > LOBS;
LOBS Observateurs;
struct CallMaj
{ void operator() (const observe_maj& obs) {obs.maj();}
};
public:
void AddObs(const POBS& p) {Observateurs.push_back(p);}
void notifyAll()
{ std::for_each(Observateurs.begin(), Observateurs.end(), CallMaj() );
}; }
17 Département Informatique
Département Informatique
typedef iut::SharedPtr<forme> PFORME;
typedef std::vector<PFORME> FORMES;
class dessin : private FORMES, public iSujet
{ //--- la classe dessin est celle vue au TD dessinateur //-- à présent elle devient un sujet
public:
void Deplace(const point& dp)
{ //--- déplaces toutes les formes...
notifyAll(); // informe les observateurs de la modif }
//--- on fait de même pour toutes les méthodes modifiant l'état };
18 Département Informatique
Département Informatique
Dans la fenêtre interface (IHM) on va trouver : class TFenPrinc : public TForm
{//--- la fiche ne change pas (cf TD dessinateur) private:
iut::POBS MajAffichage;
iut::dessin Dessin;
};
void TFenPrinc::FormCreate(TObject *Sender) {//--- on va initialiser les observateurs
MajAffichage = new observe_maj( PaintBox1 );
//--- puis on les inscrit dans le dessin dessin.AddObs( MajAffichage);
}
19 Département Informatique
Département Informatique