Accueil > .Net, C#, Design Pattern > [C# Design Pattern] Singleton

[C# Design Pattern] Singleton

Les singletons
C’est un design pattern qui mérite à être connu.
Pour deux raisons :

  1. C’est super utile
  2. Quand on connait, on l’utilise pas n’importe comment

 

J’ai déjà vu un projet où chaque couche (et y avait plus de couches que dans un paquet de pampers…) communiquait avec les autres via des singletons.
C’est mal. C’est très mal. C’est vrament très mal.

Pourquoi ?
Le singleton est fait pour qu’une seule et unique instance d’un objet soit utilisée.
La majeure partie du temps, on veut forcer la création d’une seule en même instance sans permettre l’utilisation d’autre instance.
Et pis c’est tout.

 

L’exemple souvent donné (à tort, je trouve) est la connexion à une base de données.
Mais c’est une bonne idée uniquement dans le cas d’une application lourde (utilisée par une seule et unique personne).
Par contre, dans le cadre d’une application web, si TOUS les clients utilisent la même instance…ça peut devenir une source de bugs (et pas forcément les plus simples à comprendre : pool de connexion qui ne fonctionne pas ou mal, échange de données entre plusieurs utilisateurs…).

 

En somme, le singleton devrait être conditionné à plusieurs faits (non exhaustif et entièrement de mon point de vue) :

  • On doit toujours avoir la même instance (l’instance est construite en un seul et unique endroit, impossible de le faire ailleurs)
  • Où que l’on soit dans l’application, on y accède de la même manière (c’est la classe sujette au singleton qui gère le singleton, on peut par exemple y accéder dans des couches différentes)
  • La ressource à créer est coûteuse et va être utilisée souvent (ex: chargement de données de référentiel d’une application, si c’est pour avoir deux strings constantes, alors autant utiliser une classe static)
  • Majoritairement de la lecture (moins de souci avec le thread-safe s’il n’y a pas d’écriture, et comme on ne sait pas qui va avoir accès au singleton…)

 

Autres choses à garder en mémoire :

  • Si on fait une lazy initialization, bien penser que si la première utilisation (et donc initialisation) se fait dans une partie de code avec timeout possible, il ne faut pas que l’initialisation soit trop longue
  • Si on utilise un lock, alors bien penser que les performance multi-thread seront moindre dans le cas d’un nombre d’accès importants (certes, pas forcément significatif, mais il ne faut pas le perdre de vue)
  • Si on a besoin d’une même classe dans le même état à plusieurs endroits du code, penser d’abord à du refactoring avant de faire un singleton
  • Si une seule classe a besoin du singleton…alors il y a un problème de conception
  • Au lieu de faire un singleton, penser à passer l’objet désiré en paramètre (est-ce réellement un problème de le faire ou est-ce par paresse ?)
  • Un singleton n’est pas aisément testable (là, je pense test unitaire) et peut avoir des comportements anormaux qui sont difficilement analysables (ex: accès concurent en lecture à un singleton)
  • Un singleton est accessible partout (global), donc un peu n’importe qui peut agir dessus, le n’importe qui pouvant être une partie de code qui ne devrait pas
  • Si un singleton doit prendre des paramètres, l’intérêt du singleton en prend un coup (paramètres = objets différents, donc une instance différente peut être un meilleur choix)

 

Coté code, voici l’implémentation que je préfère :

// Le sealed peut être utile pour éviter l'héritage
public sealed class SingletonClass
{
	#region Singleton
	private static readonly SingletonClass instance = new SingletonClass();
	// Retourne l'instance avec une lazy-instantiation
	public static SingletonClass Instance { get { return instance; } }
	// Constructeur static, donc l'instantiation se fait lors de la première utilisation
	static SingletonClass() { }
	// Impossible d'appeler le constructeur en dehors de la classe du singleton
	private SingletonClass() { }
	#endregion
}

 
En ce qui concerne le constructeur static, voir beforefieldinit pour plus d’informations. Voici l’extrait pertinent :

Notice that ImplicitConstructor has an additional metadata flag named beforefieldinit. This flag allows the runtime to execute the type constructor method at any time it chooses, as long as the method executes before the first access to a static field for the type. In other words, beforefieldinit gives the runtime a license to perform aggressive optimizations.

Catégories :.Net, C#, Design Pattern
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :