tags:

views:

151

answers:

3

Hi,

In my senario, I have a global setting object, say GlobalSettings, it has a static property "Current" (singleton), and there should be only one GlobalSettings instance.

But...In my data model, there's an entity "LocalizedContent":

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

In the constructor, I want to initialize the instance by setting CultureName to default culture of the system, and I can get the default culture name from GlobalSettings.Current.DefaultCultureName.

However, I don't want to use the singleton property "GlobalSettings.Current" in LocalizedContent class, since it will result in strong-coupling. So my question is, where is the right place to set this default culture name?

Thanks in advance!

+6  A: 

Why not add a constructor to LocalizedContent that takes the DefaultCultureName as a parameter?

LocalizedContent can then be re-used without a dependency on GlobalSettings.

Philip Fourie
+1 I agree with you
Javier Morillo
+1 it pretty much is the only way
James
I have considered this solution. As James said, may be it's the only solution:(.Thanks.
Dylan Lin
if `LocalizedContent`'s dependency is as simple as a single string, this is probably the best approach. however, presuming the scenario is actually more complex, see my response http://stackoverflow.com/questions/2417994/singleton-replacement/2418413#2418413
johnny g
+3  A: 

I think the trick here is to add a constructor to the LocalizedContent class which takes in the values it needs to consume.

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

For convenience sake you could also add a helper method which creates the LocalizedContent method using the GlobalSettings values.

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

Hm, you may wish to check this out.

In short, what you would like to do is, inject a "culture" source into your localized content object. Consider the following example,

// 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 () { }
}

So, a number of things. Typically singletons like this are frowned upon - they are very convenient! but as you point out, lead to tight-coupling between functional components and configuration.

If you would like to move away from this tight coupling, while preserving what exists, you have a few options, easiest being

// 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;
    }
}

If you are able to modify 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;
}

If you are not able to modify 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;
}

Now, LocalizedContent is no longer tightly-coupled to GlobalSettings singleton. In fact, any implementation of ISettings will satisfy its constructor dependency.

If your dependencies are as simple as a string or two, this may be overkill. However, if you have other complex components dependent on this global singleton, this approach may be for you :)

johnny g
Thanks for your solution.:)But, actually, I think my LocalizedContent should not be aware of the ISettings interface, since it only need to know what CultureName it should take, not the whole ISetting interface.
Dylan Lin