2010-03-10 7 views
2

Dans mon senario, j'ai un objet paramètre global, disons GlobalSettings, il a une propriété statique "Current" (singleton), et il devrait y avoir qu'une seule instance de GlobalSettings.Remplacement Singleton?

Mais ... Dans mon modèle de données, il y a une entité « LocalizedContent »:

public class LocalizedContent { 
    public string Title { get; set; } 
    public string Content { get; set; } 
    public string CultureName { get; set; } 
} 

Dans le constructeur, je veux initialiser l'instance en mettant CultureName par défaut culture du système, et je peux récupère le nom de culture par défaut de GlobalSettings.Current.DefaultCultureName.

Cependant, je ne veux pas utiliser la propriété singleton « GlobalSettings.Current » en classe LocalizedContent, car elle se traduira par couplage fort. Donc ma question est, où est le bon endroit pour définir ce nom de culture par défaut?

Merci d'avance!

+0

Essayez-vous d'éviter le couplage à 'GlobalSettings.Current' (c'est-à-dire au fait que GlobalSettings dans l'implémentation actuelle, cette classe est un singleton etc.) ou à GlobalSettings? – mjv

+0

Seule la propriété GlobalSettings.Current. Je ne suis pas capable de se moquer des GlobalSettings "Current" statiques dans mes méthodes de test unitaires. –

+0

si vous essayez de tester un singleton unitaire puis vérifiez cela http://stackoverflow.com/questions/1601073/nunit-mocking-not-working-for-singleton-method/1601438#1601438. Je résume dans ma réponse ci-dessous. –

Répondre

6

Pourquoi ne pas ajouter un constructeur à LocalizedContent qui prend le DefaultCultureName comme paramètre?

LocalizedContent peut alors être réutilisée sans dépendance à l'égard GlobalSettings.

+0

+1 Je suis d'accord avec vous – Javier

+0

+1 c'est à peu près la seule façon – James

+0

J'ai considéré cette solution. Comme James l'a dit, peut-être est-ce la seule solution :( Merci –

3

Je pense que l'astuce est ici pour ajouter un constructeur à la classe LocalizedContent qui prend les valeurs dont il a besoin de consommer.

public LocalizedContent { 
    public LocalizedContent(string cultureName) { 
    this.CultureName = cultureName; 
    } 
} 

Pour des raisons de commodité Vous pouvez également ajouter une méthode d'assistance qui crée la méthode LocalizedContent en utilisant les valeurs GlobalSettings.

public static LocalizedContent CreateLocalizedContent() { 
    return new LocalizedContent(GlobalSettings.Current.DefaultCultureName); 
} 
1

Hm, vous pouvez vérifier this sur.

En bref, ce que vous voulez faire est, injecter une source "culture" dans votre objet de contenu localisé. Considérons l'exemple suivant,

// your public global settings singleton, no big surprises here 
// except possibly thread safe locking [shrug] if you are singlethreaded 
// you can simplify this model further 
public class GlobalSettings 
{ 
    // singleton synchronization and instance reference 
    private static readonly object _instanceSyncRoot = new object(); 
    private static readonly GlobalSettings _instance = null; 

    // instance-based synchronization and values 
    private readonly object _syncRoot = new object(); 
    private string _cultureName = string.Empty; 

    // gets public static instance 
    public static GlobalSettings Current 
    { 
     get 
     { 
      lock (_instanceSyncRoot) 
      { 
       if (_instance == null) 
       { 
        _instance = new GlobalSettings(); 
       } 
      } 
      return _instance; 
     } 
    } 

    // gets public culture name 
    public string CultureName 
    { 
     get { lock (_syncRoot) { return _cultureName; } } 
     set { lock (_syncRoot) { _cultureName = value; } } 
    } 

    // private constructor to re-inforce singleton semantics 
    private GlobalSettings() { } 
} 

Ainsi, un certain nombre de choses. Typiquement singletons comme celui-ci sont mal vu - ils sont très pratiques! mais comme vous le faites remarquer, conduire à un couplage étroit entre les composants fonctionnels et la configuration.

Si vous souhaitez déplacer loin de ce couplage étroit, tout en préservant ce qui existe, vous avez quelques options, la plus facile étant

// define a general-purpose settings interface, i do not much 
// care for refactor tools, but you may use re-sharper or built in 
// refactor components to "extract" those properties from global 
// settings that you need. here we pull out culture name only, 
public interface ISettings 
{ 
    // gets culture name from underlying settings implementation 
    string CultureName { get; } 
} 

public class LocalizedContent 
{ 
    public string CultureName { get; set; } 
    public LocalizedContent (ISettings settings) 
    { 
     CultureName = settings.CultureName; 
    } 
} 

Si vous êtes en mesure de modifier GlobalSettings singleton,

// public singleton is now an implementation of a "loosely coupled 
// component" called ISettings 
public class GlobalSettings : ISettings { ... } 

// elsewhere in code 
public LocalizedContent GetLocalizedContent() 
{ 
    LocalizedContent content = new LocalizedContent (GlobalSettings.Instance); 
    return content; 
} 

Si vous n'êtes pas en mesure de modifier GlobalSettings singleton,

// wrapper class for global settings singleton 
public class Settings : ISettings 
{ 
    public string CultureName 
    { 
     get { return GlobalSettings.Instance.CultureName; } 
    } 
} 

// elsewhere in code 
public LocalizedContent GetLocalizedContent() 
{ 
    LocalizedContent content = new LocalizedContent (new Settings()); 
    return content; 
} 

Maintenant, LocalizedContent n'est plus étroitement couplé à GlobalSettings singleton. En fait, toute implémentation de ISettings satisfera sa dépendance de constructeur.

Si vos dépendances sont aussi simples comme une chaîne ou deux, cela peut être exagéré.Cependant, si vous avez d'autres composants complexes dépendant de ce singleton global, cette approche peut être pour vous :)

+0

Merci pour votre solution. :) Mais, en fait, je pense que mon LocalizedContent ne devrait pas être au courant de l'interface ISettings, car il seulement besoin de savoir ce que CultureName devrait prendre, pas toute l'interface ISetting. –