One Instance per Type

Inheritance allows derived classes share common behaviours provided by base class while have special behaviours of their own. In the mean time, maintaining single instance of types is usually a natuaral requirment. Although it looks natural and reasonable enough but it’s actually not intuitive when it comes to combine both together.

Take one real problem as an example, say we have a class DataStore, which is responsible for getting data from multiple data sources, combining the data together and produce a collection of some data type, possibly with some filter applied.

Obviously the functionality of the DataStore class is quite general purpose and we should avoid tie it up with just one type of data. Considering strong type of the collection of data, we want to make the DataStoreclass generic and derived classes can specify the its own data type/filter and share the logic of getting and combining data.

But there’s another problem we can ignore—maintain one instance of each derived DataStore class. The multiple data sources mentioned above take by the DataStore class could be from network, from database, etc, so we don’t want user create too many instance of DataStore class to waste limited resources.

So here’s the problem to solve:

  1. We need a BaseDataStore to provide common feature
  2. We derive ClassADataStore and ClassBDataStore and each overrides some virtual properties/methods of BaseDataStore
  3. We need only one instance of ClassADataStore and ClassBDataStore

To implement a singleton in C# was not hard, good examples can be found here, here and here. Hard part is how to maintain only one instance for each type.
This artical gives a solution to address the problem. It uses a generic base class with a static member of the type to store the sintance.

abstract class BaseClass<T> where T : BaseClass<T>, new()
{
  private static T obj = new T();
  public abstract string ImplementAsStatic();
  public static string CallStaticImplementation()
  {
    return obj.ImplementAsStatic();
  }
}

There’re two flaws of this approach, First one as dani calbet pointed out in the comments is that having a base class is not good sometimes-you can’t have another base class in C#, which means you can’t extend some 3rd party class by deriving from it. Secondly, it requires the class has a parameterless constructor, which is not acceptable in some cases. For example, it’s common that a static method, e.g. GetInstance, is used to pass in some paremeters needed by constructing a sintance and create/return a singleton of the class.

Well if we introduce a generic factory class, we could get all the problems solved.
First let’s say our BaseDataStore class is like this, where T is of type IData, BaseDataStore can be drived from some 3rd party class if it’s needed.

abstract class BaseDataStore<T> where T : IData
{
  protected BaseDataStore(ICollection<IDataSource> dataSources_)
  {
    DataSources = dataSources_;
  }

  public void StartGetDate()
  {
    //Subscribe to the collection of data sources and get data asynchronously
    //and only get data of DataCategory type
  }

  protected ICollection<IDataSource> DataSources { get; set; }

  public abstract string DataCategory { get; }

  public ICollection<T> GetData()
  {
     return new List<T>();
  }
}

Then we can have ClassADataStore/ClassBDataStore class like this, where IClassAData and IClassBData are of type IData, the constructor is made protected and a CreateInstance static method is used to create an instance of each class.

class ClassADataStore : BaseDataStore<IClassAData>
{
  protected ClassADataStore(ICollection<IDataSource> dataSources_)
    : base(dataSources_)
  {
  }

  public override string DataCategory
  {
     get { return "DataOfClassA"; }
  }

  public static ClassADataStore CreateInstance(ICollection<IDataSource> dataSources_)
  {
    return new ClassADataStore(dataSources_);
  }
}

class ClassBDataStore : BaseDataStore<IClassBData>
{
  protected ClassBDataStore(ICollection<IDataSource> dataSources_)
    : base(dataSources_)
  {
  }

  public override string DataCategory
  {
    get { return "DataOfClassB"; }
  }

  public static ClassBDataStore CreateInstance(ICollection<IDataSource> dataSources_)
  {
    return new ClassBDataStore(dataSources_);
  }
}

Everything is normal, these classes are just written in usual way and we can create whetever number of instances we want, but how do we control only one instance per type? The solution is to use a generic static class.

static class DataStoreFactory<TType, TData>;
  where TType : BaseDataStore<TData>;
  where TData : IData
{
  private static readonly object Mutex = new object();

  private static TType Instance { get; set; }

  public static TType GetInstance(Func<ICollection<IDataSource>, TType> instanceFactory_,
    DataSourceCreationParam param_)
  {
    //Double-check to ensure thread safe
    if (Instance == null)
    {
      lock (Mutex)
      {
        if (Instance == null)
        {
          Instance = instanceFactory_(CreateDataSources(param_));
          Instance.StartGetDate();
        }
      }
    }
    return Instance;
  }

  private static ICollection<IDataSource> CreateDataSources(DataSourceCreationParam param_)
  {
    //Create the stuff that is needed by creating a BaseDataStore<TData>
  }
}

Note here the GetInstance takes two parameters. One is a Func that takes same parameters as the constructor of BaseDataStore and return an instance of TType (BaseDataStore), which get rid of the limitation in this artical that only parameterless constructor is allowed. Another parameter is a DataSourceCreationParam, which is to demonstrate that some times preparation of construction is needed (may not be needed in real application). In this case, the BaseDataStore is expecting a ICollection parameter, where we need things like host:port, database name, etc, to build a collection of the data sources-this part of work is also generic, so we put the logic into the DataStoreFactory.

So we can get the single instance of ClassADataStore and ClassBDataStore in this way:

var classADataStore = DataStoreFactory<ClassADataStore, IClassAData>.GetInstance(
 ClassADataStore.CreateInstance, ClassADataStore.Param);
var classBDataStore = DataStoreFactory<ClassBDataStore, IClassBData>.GetInstance(
 ClassBDataStore.CreateInstance, ClassBDataStore.Param);

In a CAL application, this can be registered to a the unity container and used elsewhere.

Sample project download: VirtualStatic

Advertisements

About eagleboost

Investment Bank senior software developer
此条目发表在C#, C# Language分类目录,贴了, , , , , , 标签。将固定链接加入收藏夹。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s