9

There seems to be a stigma on SO regarding use of Singletons. I've never personally bought into it but for the sake of open mindedness I'm attempting to give IoC concepts a try as an alternative because I'm frankly bored with my everyday work and would like to try something different. Forgive me if my interpretation of IoC concepts are incorrect or misguided.

Here's the situation: I'm building a simple HttpListener based web server in a windows service that utilizes a plug-in model to determine how a request should be handled based on the URL requested (just like everyone else that asks about HttpListener). My approach to discovering the plug-ins is to query a configured directory for assemblies decorated with a HttpModuleAssemblyAttribute. These assemblies can contain 0 or more IHttpModule children who in addition are decorated with a HttpModuleAttribute used to specify the module's name, version, human readable description and various other information. Something like:

[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : IHttpModule
{
    public void Execute(HttpListenerContext context)
    {
        /* Do Something Special */
    }
}

When an HttpModule is discovered I would typically add it to a Dictionary<string, Type> object who's sole purpose is to keep track of which modules we know about. This dictionary would typically live in my variety of a Singleton which takes on the persona of an ACE style Singleton (a legacy from my C++ days where I learned about Singletons).

Now what I am trying to implement is something similar using (my understanding of) general IoC concepts. Basically what I have is an AppService collection where IAppService is defined as:

public interface IAppService : IDisposable
{
    void Initialize();
}

And my plug-in AppService would look something like:

[AppService("Plugins")]
internal class PluginAppService : IAppService, IDictionary<string, Type>
{
    /* Common IDictionary Implementation consisting of something like: */
    internal Type Item(string modName)
    {
        Type modType;
        if (!this.TryGetValue(modName, out modType)
            return null;

        return modType;
    }

    internal void Initialize()
    {
        // Find internal and external plug-ins and add them to myself
    }

    // IDisposable clean up method that attempts to dispose all known plug-ins
}

Then during service OnStart I instantiate an instance of AppServices which is locally known but passed to the constructor of all instantiated plug-ins:

public class AppServices : IDisposable, IDictionary<string, IAppService>
{
    /* Simple implementation of IDictionary */

    public void Initialization()
    {
        // Find internal IAppService implementations, instantiate them (passing this as a constructor parameter), initialize them and add them to this.

        // Somewhere in there would be something like
        Add(appSvcName, appSvc);
    }
}

Our once single method implementation becomes an abstract implementation + a constructor on the child:

[HttpModule(/*Some property values that matter */)]
public abstract class HttpModule : IHttpModule
{
    protected AppServices appServices = null;
    public HttpModule(AppServices services)
    {
        appServices = services;
    }            

    public abstract void Execute(HttpListenerContext context);
}

[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : HttpModule
{
    public SimpleHttpModule(AppServices services) : base(services) { }
    public override void Execute(HttpListenerContext context)
    {
        /* Do Something Special */
    }
}

And any access to commonly used application services becomes:

var plugType = appServices["Plugins"][plugName];

rather than:

var plugType = PluginManager.Instance[plugName];

Am I missing some basic IoC concept here that would simplify this all or is there really a benefit to all of this additional code? In my world, Singletons are simple creatures that allow code throughout a program to access needed (relatively static) information (in this case types).

To pose the questions more explicitly:

  1. Is this a valid implementation of a Factory Singleton translated to IoC/DI concepts?
  2. If it is, where is the payback/benefit for the additional code required and imposition of a seemingly more clunky API?
M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • You're probably not getting the answer you're looking for because your question is too vague (despite being so lengthy). You should really figure EXACTLY what question you want answered and ask it. – Erik Funkenbusch Jan 31 '12 at 06:50
  • I think the general backlash against singletons is due to them being a method of introducing global state into an application, which runs counter to object oriented design. They're also frequently difficult to unit test. – Daniel Mann Mar 09 '12 at 02:01
  • @DBM - Global state is counter to OOP? How so? It is generally an accepted approach in C++ (which is also OO). – M.Babcock Mar 09 '12 at 02:04

3 Answers3

4

IoC is a generic term. Dependency Injection is the more preferred term these days.

Dependency Injection really shines in several circumstances. First, it defines a more testable architecture than solutions that have hard-coded instantiations of dependencies. Singletons are difficult to unit test because they are static, and static data cannot be "unloaded".

Second, Dependency Injection not only instantiates the type you want, but all dependant types. Thus, if class A needs class B, and class B needs class C and D, then a good DI framework will automatically create all dependencies, and control their lifetimes (for instance, making them live for the lifetime of a single web request).

DI Containers can be though of as generic factories that can instantiate any kind of object (so long as it's properly configured and meets the requirments of the DI framework). So you don't have to write a custom factory.

Like with any generic solution, it's designed to give 90% of the use cases what they need. Sure, you could create a hand crafted custom linked list data structure every time you need a collection, but 90=% of the time a generic one will work just fine. The same is true of DI and Custom Factories.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • I appreciate the insight, though I'm confused how it applies to the circumstances specified in the question? I've read articles about Dependency Injection and Inversion of Control, but I've used this model across languages with positive Unit Testing experiences. Why switch now? – M.Babcock Jan 31 '12 at 05:33
  • Put differently, what about my described scenario makes DI a better choice? Am I missing the concept? – M.Babcock Jan 31 '12 at 05:44
  • 1
    It should also be noted that there is a difference between Inversion of Control (IoC) and Dependency Injection (DI), http://stackoverflow.com/questions/4596300/where-exactly-is-the-difference-between-ioc-and-di – M.Babcock Jan 31 '12 at 06:38
  • @M.Babcock - I didn't say it was a better choice. Only you can decide that. I adress your question about IoC vs Singleton factory, and I explained why Singletons are bad for testing and how DI is more generic and does more than instantiate a single object. – Erik Funkenbusch Jan 31 '12 at 06:39
  • In some ways I was hoping for a more solid answer. I realize in the end it is up to me which way to design my application. Given the fairly forceful pushes I've seen towards IoC/DI on SO, I was hoping someone could take my example into consideration and tell me why in this situation Singletons are bad. – M.Babcock Jan 31 '12 at 06:42
  • @M.Babcock - You seem to be reading more into what I said than is there. IoC is a generic term that applies to many things. Dependency Injection is the correct term for the techniqe we are discussing. IoC is not. – Erik Funkenbusch Jan 31 '12 at 06:43
3

IoC becomes more interesting when you get round to writing unit tests. Sorry to answer a question with more questions, but... What would the unit tests look like for both of your implementations? Would you be able to unit test classes that used the PluginManager without looking up assemblies from disk?

EDIT

Just because you can achieve the same functionality with singletons doesn't mean it's as easy to maintain. By using IoC (at least this style with constructors) you're explicitly stating the dependencies an object has. By using singletons that information is hidden within the class. It also makes it harder to replace those dependencies with alternate implementations.

So, with a singleton PluginManager it would difficult to test your HTTP server with mock plugins, rather it looking them up from some location on disk. With the IoC version, you could pass around an alternate version of the IAppService that just looks the plugins up from a pre-populated Dictionary.

SimonC
  • 6,590
  • 1
  • 23
  • 40
  • Based on the nature of a plug-in framework (even more specifically the one I've defined here), mocks are easy to implement because they only have to be decorated with an `HttpModuleAttribute` with the same name and implement the interface. The framework is ignorant beyond that. Realistically both implementations indicate the dependencies because they both enforce the requirement that `IHttpModule` or `HttpModule` is implemented. Possibly more concrete examples of shortcomings would help. – M.Babcock Jan 31 '12 at 05:21
  • What uses the `PluginManager` though? That's where you would see the difference IMHO. – SimonC Jan 31 '12 at 06:07
  • The framework itself uses `PluginManager` depending on the URL received. `http://server.com/moduleName/someFile.txt` would be something consumed by the framework and `moduleName` would be the name of the module registered with either `PluginManager` or `PluginAppService`. Either way its interaction with it is the same. – M.Babcock Jan 31 '12 at 06:11
0

While I'm still not really convinced that IoC/DI is better in this situation, I definitely have seen benefit as the project's scope crept. For things like logging and configurability it most certainly is the right approach.

I look forward to experimenting with it more in future projects.

M.Babcock
  • 18,753
  • 6
  • 54
  • 84