views:

132

answers:

2

I'm having a bit of trouble figuring out how to manage the internal dependencies that I have in a SDK I'm developing, for some reason everything I try just seems to be hard to work with.

Say I have these classes:

class Table
{
   Table(string name,IQueryProvider provider, IComObject comobject) {}
}

class TableFactory
{
   Table BuildTable(name) <- builds a table object.
}

the problem that I'm having is that BuildTable() method has to create a IQueryProvider and a IComObject and pass the name down. I have implemented what I hope(if I understand it correctly) the service locater pattern but if I use something like this:

BuildTable(string name)
{
   IQueryProvider provider = ServiceLocator.GetInstance<IQueryProvider>();
   IComObject comobject = ServiceLocator.GetInstance<IComObject>();
   Table tab = new Table(name,provider,comobject);
   return tab;
}

It now means that I have to have both IQueryProvider and IComObject in the ServiceLocator which makes my dependencies hard to see and test. So I created a dependency factory to create different types of objects and factories something like this:

class DependencyFactory
{
    Table BuildTable(string name)
    {
        //call other BuildMethods to create objects.
        //return new Table.
    }

    //Other Build methods for things like IQueryProvider, IComObject.
}

then I only have to register DependencyFactory in my service locater and then just call build methods.

Does any of this smell bad to you?

Is my first BuildTable method ok, or am I right being concerned with it.

A: 

To make testing easier, you might want to use dependency injection to provide the factories used to create the dependent objects. I'd prefer this over a single factory for all types of objects. For example, you might have a TableFactory for Tables. This would have two constructors -- the default one which causes the correct factories to be instantiated and another constructor that allows the dependent object factories to be injected. Use the first in your production code, but the second in your tests.

 public class TableFactory
 {
      private ServiceLocator Locator { get; set; }

      public TableFactory() : this(null) { }

      public TableFactory( ServiceLocator locator )
      {
           this.Locator = locator ?? new ServiceLocator();
      }

      public Table BuildTable( string name )
      {
          IQueryProvider provider = ServiceLocator.GetInstance<IQueryProvider>();
          IComObject comobject = ServiceLocator.GetInstance<IComObject>();
          return new Table(name,provider,comobject);
      }
 }

You would do something similar with the ServiceLocator class, but inject instances of IQueryProvider and IComObject as needed.

Note that this is a very simple type of injection. If you wanted to, you could also have the default versions of the dependent objects provided via a configuration file. In that case, you might only have the default constructor and use property injection to provide the dependent objects. The properties would probably be marked with the setters having internal visibility so that your configuration class can create the factories and set their properties correctly. You could then use InternalsVisibleTo to make the setters available to your tests and do the same there.

tvanfosson
but it still doesn't really solve the problem of not being able to see the internal dependencies. But then again it is internal to the SDK so I do know what types I need. hmm what to do.
Nathan W
A: 

I consider Service Locator an anti-pattern for exactly the reason you describe: It makes it very hard to figure out how to use a type correctly. Instead of Service Locator, I'd recommend using Dependency Injection (DI); for example (but not limited to) Constructor Injection. You can do it manually, like described in this blog post, or you can let a DI Container do the work for you.

Common DI Container are:

Mark Seemann
I do use DI as much as possible, but my main problem is where and how to I create all the objects to use. So I make factory objects but then where do I create those factory objects.
Nathan W
@Nathan W: The short answer is: When you need them. The more complex answer is: At the top (or root, depending on your view) of the application stack. The place where you wire everything together. You can do it manually, like I describe in my blog post above, or you can let a DI Container do it for you... I expect that we are moving a bit in circles here, so there may be a dimension of your question that I'm missing...
Mark Seemann
I agree. You basically configure your app at root (or thereabouts). I've heard IoC referred to as "parametrise from above", and that's pretty much it -- the dependency creation/injection is configured at the next level up.The service locator pattern is almost like a poor man's DI in one sense; I tried it in the past and it makes code hard due to hidden dependencies. If you use a dedicated IoC container, your 99% of your code remains ignorant of its existence, but the application is really easy to change + configure, plus your can test everything til your heart's content.
Mark Simpson