Design pattern Singleton avec .Net (VB.Net et C#)

Ronald VASSEUR

Les design patterns sont nombreux et plus ou moins connus, au cours de cet article je vais vous présenter le plus connu et populaire, il s'agît du Singleton. Ce pattern à pour but de contrôler et de limiter à une ou quelques-unes le nombre d'instances pour une classe donnée. Voyons sans plus tarder comment implémenter ce pattern en .Net (Visual Basic 2005 et C# 2.0).

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Design pattern Singleton avec .Net (VB.Net et C#)

- Auteur : Ronald VASSEUR

- Parution : 10 juillet 2007

- Licence : Copyright ® 2007 Ronald Vasseur. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Introduction

- I. Présentation du pattern singleton

- II. Utiliser le pattern singleton avec .Net

- Conclusion

Introduction

Tout d'abord je pense qu'il est utile de préciser ce qu'est un pattern, il s'agit en fait d'un modèle utilisé en génie logiciel. Un pattern est donc une manière de procéder en programmation, il définit comment doivent s'articuler certains éléments d'un bout de code ou d'une application. Il existe des dizaines de patterns couvrant de très nombreux domaines du génie logiciel. Ils sont le plus souvent créés grâce à des retours d'expérience, et des moyens de procéder largement éprouvés et reconnus, ils peuvent s'apparenter aux « bonnes manières de faire ». Les design patterns sont un ensemble de patterns s'intéressant à la conception de logiciels, comment ils doivent être conçus et quels modèles ils doivent reprendre. Dans cet article nous allons étudier un pattern très connu et très utilisé, il s'agit du Singleton. Il est tellement utilisé que peut-être vous l'avez déjà utilisé sans même le savoir.

I - Présentation du pattern singleton

Le Singleton est un modèle de conception. Son but est relativement simple, il doit permettre de limiter le nombre d'instances d'une classe à une ou quelques-unes. En effet, dans de nombreuses occasions nous devons être sûrs qu'il n'existe pas plus d'une seule instance pour une classe donnée, cela peut être par exemple le cas d'un objet gérant une connexion à une base de données, ou encore d'un objet écrivant dans un fichier de log… Le Singleton, par un principe simple qui va nous permettre de s'assurer qu'il n'existe qu'une seule instance d'une classe donnée, en fait c'est le Singleton lui-même qui effectue cet autocontrôle et va agir en fonction de la situation. Ainsi, si aucune instance n'existe alors une instance sera créée, et, si une instance existe déjà alors, il nous retournera une référence vers cette instance déjà existante.

La caractéristique majeure d'un Singleton est que le constructeur de cette classe est privé (Private en VB .Net), il n'est donc pas accessible comme doit normalement l'être le constructeur d'une classe. En effet, dans l'ordre normal des choses un constructeur doit être public, pour qu'on puisse instancier notre classe en appelant sa méthode New().

Puisque le constructeur n'est pas Public, il va falloir avoir au moins une méthode publique pour que l'on puisse utiliser notre Singleton. C'est cette méthode publique qui concrètement va vérifier si une instance existe déjà ou non. Cette méthode, sera le seul et unique moyen pour un développeur, et donc une application, de créer une instance de cette classe implémentant le design pattern Singleton.

Un autre détail qui a son importance c'est le fait que l'on doive déclarer une variable locale qui aura pour rôle de stocker la référence à l'instance unique de notre Singleton. Regardons maintenant comment concrètement implémenter le pattern Singleton avec Visual Basic 2005 et C# 2.0.

II - Utiliser le pattern singleton avec .Net

Voici un exemple de classe implémentant le pattern Singleton :

Implémentation du design pattern Singleton en VB.Net

Option Explicit On
Option Strict On

Public Class MonSingleton

' Variable locale pour stocker une référence vers l'instance
Private Shared instance As MonSingleton = Nothing

' Le constructeur est Private
Private Sub New()
'
End Sub

' La méthode GetInstance doit être Shared
Public Shared Function GetInstance() As MonSingleton

' Si pas d'instance existante on en crée une...
If instance Is Nothing Then
instance = New MonSingleton
End If

' On retourne l'instance de MonSingleton
Return instance

End Function

End Class

Implémentation du design pattern Singleton en C#

public class MonSingleton
{

// Variable locale pour stocker une référence vers l'instance
private static MonSingleton instance = null;

// Le constructeur est Private
private MonSingleton()
{
//
}

// La méthode GetInstance doit être Static
public static MonSingleton GetInstance()
{

// Si pas d'instance existante on en crée une...
if (instance == null)
{
instance = new MonSingleton();
}

// On retourne l'instance de MonSingleton
return instance;

}

}

L'implémentation ci-dessus du Pattern Singleton est correcte, mais présente un petit défaut, en effet, elle n'est pas thread-safe, c'est-à-dire que rien n'empêche plusieurs thread d'utiliser une instance de notre Singleton en même temps. Pour contourner ce problème, nous allons utiliser un mot clé Synlock en VB.Net et Lock en C# pour faire en sorte qu'une telle chose ne soit pas possible. Voyons sans plus tarder comment appliquer cela dans le code :

Implémentation du design pattern Singleton thread-safe en VB.Net

Option Explicit On
Option Strict On

Public Class MonSingleton

' Variable locale pour stocker une référence vers l'instance
Private Shared instance As MonSingleton = Nothing
Private Shared ReadOnly mylock As New Object()

' Le constructeur est Private
Private Sub New()
'
End Sub

' La méthode GetInstance doit être Shared
Public Shared Function GetInstance() As MonSingleton

Synclock(mylock)
' Si pas d'instance existante on en crée une...
If instance Is Nothing Then
instance = New MonSingleton
End If

' On retourne l'instance de MonSingleton
Return instance
End Synclock

End Function

End Class

Implémentation du design pattern Singleton thread-safe en C#

public class MonSingleton
{

// Variable locale pour stocker une référence vers l'instance
private static MonSingleton instance = null;
private static readonly object mylock = new object();

// Le constructeur est Private
private MonSingleton()
{
//
}

// La méthode GetInstance doit être Shared
public static MonSingleton GetInstance()
{

lock ((mylock)) {
// Si pas d'instance existante on en crée une...
if (instance == null)
{
instance = new MonSingleton();
}

// On retourne l'instance de MonSingleton
return instance;
}

}

}

Désormais notre Singleton est réellement thread-safe et il est impossible, même à des threads différents, de créer plusieurs instances de cette classe, il y a une vérification lors de l'instanciation et un verrou en mémoire sur l'objet.

Remarque : attention, Synlock/Lock est certes efficace pour rendre notre Singleton thread-safe mais il faut garder à l'esprit que cette manière de procéder peut suivant les conditions être couteuse en termes de performances, comme d'habitude en développement il faudra donc évaluer le pour et le contre. Mais, à mon avis, cette légère perte de performance vaut largement la sécurité et la robustesse que l'on apporte à notre Singleton.

Maintenant que notre Singleton est implémenté, voyons comment le démontrer. Pour cela, voici le code d'une petite application console qui va vérifier que tous les objets de types MonSingleton correspondent bien à une seule et même instance. Pour cela nous allons utiliser la méthode Equals() que chaque objet .Net implémente et qui permet de comparer l'égalité ou non de deux objets.

Application console de test du Singleton en VB.Net

Option Explicit On
Option Strict On

Imports SingletonDeveloppez

Module Module1

Sub Main()

Dim objet1 As MonSingleton = Nothing
objet1 = MonSingleton.GetInstance()

Dim objet2 As MonSingleton = Nothing
objet2 = MonSingleton.GetInstance()

If objet1.Equals(objet2) Then
' Notre Singleton fonctionne, il n'y à qu'une seule instance.
Console.WriteLine("Les deux instances sont identiques.")
End If

Console.ReadLine()

End Sub

End Module

Application console de test du Singleton en C#

using SingletonDeveloppez;

class Module1
{

public void Main()
{

MonSingleton objet1 = null;
objet1 = MonSingleton.GetInstance();

MonSingleton objet2 = null;
objet2 = MonSingleton.GetInstance();

if (objet1.Equals(objet2))
{
// Notre Singleton fonctionne, il n'y a qu'une seule instance.
Console.WriteLine("Les deux instances sont identiques.");
}

Console.ReadLine();

}

}

Conclusion

Nous avons vu dans cet article une manière d'implémenter simplement le design pattern Singleton avec VB.Net et en C#. En suivant ce pattern vous voyez à quel point il est simple de contrôler le nombre d'instances qui peuvent exister au même moment pour une classe donnée. Toute l'astuce de ce pattern est que le contrôle du nombre d'instances s'effectue au sein même de la classe et non par un code annexe qui le rendrait peu fiable. Un autre avantage qu'apporte ce pattern est le fait que la modification du processus d'instanciation de la classe n'aura pas d'impact sur le reste de l'application et du code existant, en effet l'instanciation de la classe est entièrement gérée par la classe elle-même grâce au constructeur Private et à la méthode GetInstance().

Un grand merci à AGM26 pour la relecture de cet article, ainsi qu'à l'équipe Dotnet pour son aide.