I. Qu'est-ce qu'un design pattern▲
Depuis les débuts de la programmation, les développeurs ont rencontré différents problèmes de conception. La plupart de ces problèmes sont récurrents. Pour éviter aux autres développeurs de buter sur les mêmes soucis, certains groupes de développeurs ont développé ce qu'on appelle des design patterns (ou patrons de conception en français). Un design pattern est une solution à un problème récurrent dans la conception d'applications orientées objet. Il décrit alors une solution standard, utilisable dans la conception de logiciel.
II. Le Design Pattern : Singleton▲
II-A. Description du problème▲
Un grand nombre d'applications possèdent des classes qui doivent (ou peuvent) être instanciées une seule et unique fois :
- connexion à une BDD ;
- gestion des fichiers de journalisation ;
- lecture d'un fichier INI ;
- récupération de paramètres généraux.
Par exemple, instancier plusieurs fois une classe lisant un fichier de configuration n'a aucun intérêt. Alors, la question est : « comment instancier une seule fois une classe utilisée plusieurs fois ?» La solution fréquemment adoptée est la gestion d'une variable globale au lancement du programme. Cette solution est à éviter pour les raisons suivantes :
- respect de l'encapsulation ;
- éviter aux développeurs de créer une autre instance ;
- ne pas multiplier les variables globales dont le chargement au départ aura un impact sur les performances de l'application ;
- maintenance ingérable en cas de multiplications de variables et de travail en équipe.
Le design pattern Singleton répond à l'ensemble de ces problèmes.
II-B. La solution▲
La solution que nous allons mettre en place devra :
- permettre au développeur de récupérer l'instance de l'objet (instanciation à la demande) ;
- empêcher le développeur de créer une nouvelle instance ;
- contrôler le nombre d'instances.
III. Programmation du design Pattern▲
Passons à la pratique. Nous allons créer une classe pc_bdd qui gère la connexion à une base de données (BDD). Pour cet exemple, la classe sera composée d'un seul attribut lo_bdd de type pc_bdd.
III-A. Récupérer l'instance▲
2.
3.
4.
pc_bdd est
une
Classe
PRIVÉE
GLOBALE
lo_bdd est
un
pc_bdd dynamique
FIN
2.
PROCEDURE
GLOBALE GetInstance()
RENVOYER
::
lo_bdd
Nous venons de mettre en place la classe avec la fonction GetInstance() qui permet au développeur de récupérer l'instance de la classe. L'attribut lo_bdd est privé pour forcer le programmeur à utiliser la procédure GetInstance(). Comme celle-ci est globale (statique), lo_bdd est global (statique) par la même occasion.
Rappel : Une procédure globale (statique) ne peut manipuler que des variables globales (statiques).
Ne pas confondre le type global (statique) d'une méthode de classe et de ses membres et l'avantage du singleton d'éviter la multiplication de variables globales au projet qui se chargent au lancement du programme.
III-B. Interdire une nouvelle instance▲
Pour empêcher les développeurs d'instancier un objet, nous allons déclarer son constructeur en privé
2.
PROCEDURE
PRIVÉE
Constructeur()
// À cet endroit on peut y insérer le code de connexion
Le constructeur déclaré dans cet exemple ne contient pas de code, libre à vous d'y insérer votre code de connexion à votre BDD.
2.
3.
4.
5.
6.
7.
8.
// Connexion à une base MySQL en utilisant un driver JDBC :
SQLConnecte
(
"jdbc:mysql://"
+
monServeur +
"/"
+
madatabase,
"moi"
,
"monpwd"
,
""
,
"JDBC"
,
"com.mysql.jdbc.Driver"
)
// Connexion en utilisant une source de données ODBC :
SQLConnecte
(
"jdbc:odbc:MaSource"
,
"moi"
,
"monpwd"
,
""
,
"JDBC"
,
"sun.jdbc.odbc.JdbcOdbcDriver"
)
// Connexion en utilisant HyperFileSQL :
SQLConnecte
(
"MonAnalyse.WDD"
,
""
,
"monpwd"
,
""
,
"HyperFileSQL"
)
III-C. Contrôler le nombre d'instances▲
Pour contrôler le nombre d'instances, nous allons intervenir sur la procédure GetInstance() en y ajoutant un test. Le test consiste à comparer notre attribut à Null et créer une instance le cas échéant.
2.
3.
4.
5.
PROCEDURE
GLOBALE GetInstance()
SI
::
lo_bdd=
Null
ALORS
::
lo_bdd=
allouer
un
pc_bdd()
FIN
RENVOYER
::
lo_bdd
III-D. Exemple d'utilisation▲
Dans ce paragraphe, nous allons voir le code d'utilisation de cette classe.
lo_bdd est
un
pc_bdd dynamique
=
pc_bdd::
GetInstance()
III-E. Encore un effort et c'est bon…▲
Nous venons de programmer une classe selon le design pattern singleton, mais dans la démarche intellectuelle, nous sommes partis du principe que la procédure GetInstance() n'est jamais exécutée plusieurs fois en même temps. En effet, nous n'avons pas pris en compte les logiciels multithread. En l'état actuel, dans le cas d'un programme multithread, on peut se retrouver avec plusieurs instances de pc_bdd. Comment est-ce possible ? L'origine du problème est l'exécution en parallèle des processus, deux processus exécutent en même temps la procédure GetInstance(), et constatent que l'attribut lo_bdd est Null, ils vont donc créer chacun une instance. L'astuce consiste à permettre une seule exécution par un seul processus. Pour cela, WinDev dispose des fonctions : SectionCritiqueDébut() et SectionCritiqueFin() pour encadrer le code à bloquer.
Nous allons effectuer ce blocage dans la procédure GetInstance() pour ne pas impacter les développeurs.
2.
3.
4.
5.
6.
7.
PROCEDURE
GLOBALE GetInstance()
SectionCritiqueDébut
()
SI
::
lo_bdd=
Null
ALORS
::
lo_bdd=
allouer
un
pc_bdd()
FIN
SectionCritiqueFin
()
RENVOYER
::
lo_bdd
IV. Conclusion▲
Cet article explique les avantages et l'implémentation du design pattern « Singleton » dans WinDev. Mais celui-ci ne fait pas l'unanimité en programmation, il est souvent qualifié d'antipattern, car il est difficilement testable, souvent mal utilisé et empêche l'évolutivité d'une application. Il est souvent utilisé pour regrouper des variables globales dans une classe.
Remerciements :
- relecture technique : littlewhite ;
- relecture orthographique : ced ;
- vérification avant mise en ligne : djibril