views:

716

answers:

5

Hi,

I am currently designing a class library that will provide data to a web application graph rendering engine in C#. I am currently defining the interfaces of this library.

I have a IGraphData interface which I would like to cache using a service that accesses the cache, this is called IGraphDataCacheService and has set and get methods to add and retrieve IGraphData objects to and from the cache. the cache service will be a singleton.

I am confused about the correct way to implement this, so that there is only one cache service that can get and set generic IgraphData objects.

I came up with this:

interface IGraphDataCacheService {

IGraphData<object> Get(string identifier);
void Set(IGraphData<object> graphData);}

or this:

T Get<T, P>(string identifier) where T : IGraphData<P>;
void Set<T,P>(T graphData) where T : IGraphData<P>;

Can any one offer any advice help?

Thanks

+1  A: 

A few points:

  1. I'd probably rename the interface methods to be more emblematic of a caching service. For example, Fetch and Store instead of Get and Set, which makes it sound like you're getting or setting the provider rather than the data to be cached.

  2. Ensuring that there is only one cache is an implementation detail, not an interface one.

To implement a singleton, try something like:

public class SingletonCacheService : IGraphDataCacheService {
   private static Singleton instance;

   private Singleton() {}

   // snip implementation of IGraphDataCacheService methods ...

   public static Singleton Instance {
      get {
         if (instance == null) {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

Note that this simple version isn't threadsafe.

John Feminella
See below for how to accomplish this with a static initializer instead.
Matthew Flaschen
+1  A: 

Both alternatives seem plausible at a glance; my hunch is that you need to write some 'typical' client code to decide. e.g. Does the typical client 'know' the type of data associated with the identifier it's looking up? Good API design requires identifying the main use scenarios and using that to inform the design.

Brian
A: 

You can't ensure there's only a single instance implementing an interface. However, you can make a class (e.g. GraphDataCacheServiceImpl) implementing the interface a singleton by sealing it and providing only a getter property, with the object created as a static variable from a private constructor. See the below. As far as generics, it's not exactly clear what you're seeking to accomplish. But I would guess the below is close to what you want.

interface IGraphDataCacheService<T> {

IGraphData<T> Get(string identifier);
void Set(IGraphData<T> graphData);

}


public sealed class GraphDataCacheServiceImpl<T> : IGraphDataCacheService<T>
{

  private GraphDataCacheServiceImpl()
  {
      // ..
  }

  static GraphDataCacheServiceImpl()
  {
    Instance = new GraphDataCacheServiceImpl<T>();
  }

  public IGraphData<T> Get(string id)
  {
      return new GraphDataImpl<T>();
  }

  public void Set(IGraphData<T> graphData)
  {
  }

  public static GraphDataCacheServiceImpl<T> Instance {get; private set;}
}
Matthew Flaschen
+2  A: 

Why don't you just make the interface generic instead?

interface ICacheService<T> {
    T Get(string identifier);
    void Set(T graphData);
}

if you wanted, you could type-constrain T to be of type IGraphData, or you could write it as:

interface IGraphDataCacheService<T> {
    IGraphData<T> Get(string identifier);
    void Set(IGraphData<T> graphData);
}
Paul Betts
+1  A: 

If I understand your question correctly you are wanting to treat the generic types like they are the same, but in current .NET implementation you can't do this.

IGraphData<string> can't be passed as a IGraphData<object> they are actually different types even though string is an object, the generic types are not related and can't be cast or passed like they are the same.

If you control the IGraphData interface you can create a IGraphData interface and derive IGraphData from it and use IGraphData to access the cache. It just depends on how you are using it and what you have the control over.

You can do what you want in C# 4.0. There is an article about it here

Brian ONeil