Settings Objects

Configurability is a member of the *-ability gang. Influence how your components work at runtime. Store configuration information somewhere. Either in code, in a config file or in some type of database. Load it at runtime and use it in your application.

You can hard-code the way these values are retrieved directly in your components. But that makes these components hard to test and limits your flexibility (another gang-member) later in the process. You can hand them to your component one-by-one either as constructor parameters or method parameters which tends to get very noisy if you need more than a few values. The Parameter Object Refactoring can reduce that noise a lot. Yet it does not solve the problem of how to get the values into that parameter object.

You can use some custom ConfigurationManager or extend the .NET configuration engine. But what about defining defaults for your configuration? Defaults for different scenarios maybe? Meaningful defaults can greatly reduce the clutter in your configuration.

I found another approach very useful: Encapsulate the way your configuration values are retrieved along with the defaults into something I call Settings Objects.

They contain a number of public virtual properties that are used to retrieve the desired configuration values.

public class DemoSettings
{
  private static class Defaults
  {
    public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
  }
  public virtual TimeSpan Timeout
  {
    get
    {
      string key = GetKeyFor(() => Timeout);
      string valueFromConfigFile = ConfigurationManager.AppSettings[key];
      TimeSpan ts;
      if (!string.IsNullOrEmpty(valueFromConfigFile) &&
          TimeSpan.TryParse(valueFromConfigFile, CultureInfo.CurrentCulture, out ts))
      {
        return ts;
      }
      return Defaults.Timeout;
    }
  }
  private string GetKeyFor(Expression<Func> memberSelector)
  {
    MemberExpression expression = (MemberExpression) memberSelector.Body;
    string key = this.GetType().Name + "." + expression.Member.Name;
    return key;
  }
}

The code in this demo tries to load configuration values from a config file following an easy convention: Values are stored asĀ <NameOfTheSettingsClass>.<PropertyName> in the appSettings section.

If no value is found or the value does not meet the format expectations a hard-coded default is used. If performance is critical you can use a private member or any kind of cache to store the retrieved value. You can implement expiration for your cached values if you want to.

If you derive from DemoSettings you can use different default values in your derived classes. Or you can entirely hard-code values. Which is especially useful for testing scenarios. Or you can turn that procedure upside-down and design a base-class that uses hard-coded values (e.g. in the beginning of the development of a new component) and derive settings objects with different methods for information retrieval later as needed.

You can read from a database instead of a config file. Or you can implement a “three strikes” scenario: Check wether the value is defined in your local config file. If not check the database. If there is no value in the database use the default. By this, values in a config file override values in a database which in turn override the hard-coded defaults.

Btw.: If you are using a database caching might make sense to avoid too many round-trips. And if you are already on .NET 4.5 you can use the CallerMemberNameAttribute instead of an Expression to figure out the name of the property you want to retrieve.

You can argue that Settings Objects violate the Single Responsibility Principle and I agree with you. A settings DTO and a separate Loader class of some kind are more SRP’ish. But in this case I value the improved useability of settings objects more than following SRP. They are small, self-contained objects. If you don’t need the additional configurability stick with hard-coded values. That minimizes the effort until you really have a need for that flexibility (and someone is willing to pay you for it). You don’t have to write another “framework” to fill your settings objects with values from various sources. You can decide on a case-by-case basis wether you need support for config-files or databases or something else or if hard-coded values are good enough for now. I like this approach and it served me well on several projects.