Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Benefit of IoC over my Factory Singleton

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?
like image 636
M.Babcock Avatar asked Jan 31 '12 03:01

M.Babcock


2 Answers

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.

like image 193
Erik Funkenbusch Avatar answered Sep 20 '22 01:09

Erik Funkenbusch


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.

like image 24
SimonC Avatar answered Sep 23 '22 01:09

SimonC