开发者

Implementation specific methods on an interface - how can I avoid them?

  • We have an interface that is passed to the constructor using an IoC container
  • We have multiple classes that implement this interface.

The problem is, some (not all) of the implementations need to be cleaned up, preferably using the IDisposa开发者_如何学Pythonble interface.

Since not all of the implementations will need the "Dispose" method, do I include it anyway (I realize I'm not actually implementing IDisposable, this is just an example)? To me, it seems like the class that has knowledge about the actual implementations of the interface should know how to clean up those objects. However, this is the IoC container, and I see no elegant way of signalling it to call cleanup on a particular class it has created.

interface IBar
{
  void DoWork();
  void Dispose(); //Not all implementations need this
}

class Foo : IBar
{
  public void DoWork()
  {
    //implementation 1
  }

  public void Dispose()
  {
    //cleanup, which needs to be called
  }
}

class Foo2 : IBar
{
  public void DoWork()
  {
    //implementation 2
  }

  public void Dispose()
  {
    //No cleanup needed in this implementation
  }
}


You can implement IDisposable on the ones that need it and then do this:

interface IBar
{
    void Bar();
}

class Foo : IBar
{
    public void Bar() { }
}

class Foo2 : IBar, IDisposable
{
    public void Bar() {}
    public void Dispose() {}
}

class Program
{
    static void Main(string[] args)
    {
        foreach (IBar bar in new IBar[] { new Foo(), new Foo2() })
        {
            bar.Bar();

            IDisposable disp = bar as IDisposable;

            if (disp != null)
            {
                disp.Dispose();
            }
        }
    }
}


What's wrong with the following?

interface IBar { void DoWork(); }
class Foo : IBar, IDisposable { // details, implements Dispose }
class Foo2 : IBar { // details, no Dispose }


Odd question. If your concern is specifically IDisposable, most containers will inspect the implementation, and if it contains an implementation of IDisposable the container will invoke Dispose () auto-magically when it releases the instance.

So,

// pure clean business interface. no dispose member
public interface ISomeBusinessInterface { }

// an implementation that does not require any 'disposing'
// and implements *only* business interface
public class SomeBusinessImplementation : 
    ISomeBusinessInterface
{
}

// an implementation that contains heavy resources and 
// requires 'disposing'
public class SomeDisposableBusinessImplementation : 
    ISomeBusinessInterface,
    IDisposable
{
}

// a typical consumer. no knowledge of instance life-cycle
public class SomeConsumer 
{
    // injector constructor
    public SomeConsumer (ISomeBusinessInterface business) { }
}

Your consumers should have no knowledge of an injected component's lifecycle, and is not in any shape way or form responsible for it. From this perspective, it makes sense that ISomeBusinessInterface does not inherit or expose any such Dispose method - I mean, consider if you disposed of an important singleton service! How embarassing would that be?

So who is responsible for the life-cycle of injected components? Well the container is of course! As previously mentioned, most containers will inspect component implementations for implementations of IDisposable [also another reason to use standard CLR-defined interfaces], and will invoke Dispose when it determines the component has reached the end of its life. So SomeDisposableBusinessImplementation will be properly disposed of in the end. :)


Castle Windsor

Castle Windsor IoC container tracks and maintains references to all instances it creates. It uses reflection to determine if the instance is "disposable" or not, and then invokes Dispose () when the instance's lifecycle completes. For long-lived objects, this is when the container is disposed. For transient short-lived objects, this is when Release (object) is explicitly invoked by consumer *.

*=which raises an important point, transient objects should be acquired via an explicit call to Resolve<T> () and released via an explicit call to Release (object). Implicitly injecting transient classes may lead to memory leaks!


StructureMap

StructureMap IoC container does not track and maintain references to transient instances. Which pretty much means object lifecycle management is a little complex +. This is great for transient short-lived objects, as there is no additional book-keeping required except the usual. However, for long-lived objects, you will have to extend the framework, reflect, and dispose of instances yourself.

+=complex imo.


When does Dispose need to be called? After performing work or during application shutdown? If the latter, doesn't your IoC container support such cleanup? In Java and with the Spring IoC container, your implementation would implement Spring's disposable interface, and the container would invoke the dispose method during shutdown.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜