views:

129

answers:

7

I have an interface defined as IStore, with two methods:

public interface IStore<TEntity>
{
    TEntity Get(object identifier);
    void Put(TEntity entity);
}

I want an event to be raised on the success of Put (for reference, Put could store a row in a db, or file on the file system etc...)

So, a class implementing Istore for type of Product would look a bit like this:

class MyStore : IStore<Product>
{
    public Product Get(object identifier)
    {
        //whatever
    }

    public void Put(Product entity)
    {
        //Store the product in db
        //RAISE EVENT ON SUCCESS
    }
}

What i'm after, is a way of ensuring that every implementation of IStore raises the event - should i have an abstract class instead, or as well as, the interface?

+1  A: 

Yes, you should use an abstract class instead of an interface.

If you decide to use an abstract class implementing your interface, it will not prevent other developpers from implementing their own version of the interface, which will eventually not raise the event.

That being said, even using an abstract class will not force other developpers to use your method, as they could overwrite it.

I think the best method would be to use a template method :

public abstract class AbstractStore<TEntity>
{
    public TEntity Get(object identifier);
    public sealed void Put(TEntity entity)
    {
        if (DoPut(entity))
        {
            // raise event
        }
    }

    protected abstract bool DoPut(TEntity entity);
}

The real store will have to implement the DoPut method, returning a boolean indicating if the Put operation was successful. The event will be raised from the Put method, which is publicly visible.

Thibault Falise
It doesn't stop inheriting classes from overriding the behaviour, either. There's no real way of guaranteeing this behaviour.
Dan Puzey
+3  A: 

There is really no way of ensuring that every implementation of IStore raises an event. You could have an abstract class that has the put method, but that still doesn't mean that you could have a put method in a subclass of the abstract class that totally ignores the abstract class's method.

In the end, the best way to encourage the raising of the event is to write the method the developer should use, by way of an abstract class. That way, they have to go out of their way not to use it

phsr
+8  A: 

my suggestion:

public abstract class Store<TEntity>
{
    public abstract TEntity Get(object identifier);
    public void Put(TEntity entity)
    {
        //Do actions before call
        InternalPut(entity);
        //Raise event or other postprocessing
    }

    protected abstract void InternalPut(TEntity entity);
}

then override InternalPut in your class

Andrey
This was going to be my answer. At some level, you are always dependent on the people understanding the contract when you allow methods to be overridden. But providing a framework like this allows you to have some control about the order things are allowed to happen.
unholysampler
+2  A: 

You need to have abstract class implementing Put method from your interface. Also you can add abstract method like PutImpl, something like this:

public abstract class MyStoreBase : IStore<TEntity>
{
    public abstract TEntity Get(object identifier);

    public abstract void PutImpl(TEntity entity);

    public void Put(TEntity entity)
    {
        // Each inheritor will implement this method.
        PutImpl(entity);

        // But event is fired in base class.
        FireEvent();
    }
}
Andrew Bezzub
A: 

If you really need to use an interface, then new PostSharp 2 can do aspect inheritance. Unfortunately, to get that feature you need to purchase at least a personal license at $200.

With such an aspect, you would place it on the method declared in your interface, and all implementations of your interface would inherit it.

Lasse V. Karlsen
A: 
 abstract class Store<TEntity>
{
     public abstract TEntity Get(object identifier);
     protected abstract void Put(TEntity entity);
     public void PutBase(TEntity entity)
     {
         Put(entity);
         //Raise event here
     }

}

some note: I did Put as protected so all derived class must implement it but a client code cannot invoke it.

Arseny
A: 

Writing an abstract class to wrap a PutInner-like method is absolutely the best way to approach this, as others have already suggested.

I would just add that if you want for any implementation of IStore<TEntity> to always raise an event when Put is called, I would recommend also adding said event to the interface itself:

public interface IStore<TEntity>
{
    event EventHandler<EntityEventArgs<TEntity>> EntityAdded;

    TEntity Get(object identifier);
    void Put(TEntity entity);
}

public EntityEventArgs<TEntity> : EventArgs
{
    public TEntity Entity { get; set; }
}

This doesn't force implementors to do anything; but it does clearly establish an expectation that EntityAdded will be raised on a successful Put.

Dan Tao