4

I'm wondering how to properly use abstract factories when using a DI framework and one of the parameters in that factory is a dependency that should be handled by the DI framework.

I am not sure whether to make my abstract factory omit the parameter completely then use my DI container to wire it up or whether I should pass the dependency to the object.

For example, I have a TcpServer and it uses a Session.Factory to create sockets. The Session object actually takes a Processor in its constructor. Should I pass the Processor to the TcpServer then have it pass it onto the Session.Factory or have my DI container do the wiring?

If I were to have the DI container do the wiring it would look like this:

class Session : ISession
{
    public delegate ISession Factory(string name);

    ...
    public Session(string name, Processor processor)
    {
      ...
    }
}

class TcpServer : ITcpServer
{
    private readonly Session.Factory _sessionFactory;

    public TcpServer(Session.Factory sessionFactory)
    {
        this._sessionFactory = socketFactory;
    }

    ...

    public void OnConnectionReceived()
    {
       ...
       var session= _sessionFactory(ip.LocalEndPoint());
       ...

    }
}

Then using a DI container like Ninject I'd be able to do this when configuring the container:

Bind<Session.Factory>().ToMethod(c =>
{
    var processor = Kernel.Get<Processor>();
    return (name) => new Session(name, processor);
}).InSingletonScope();

My main issue with this approach is that it assumes whoever creates the Session.Factory knows about the processor. In my case, since I am using a DI container, this is actually very convenient but it seems weird to have a factory have its own dependencies. I always imagined a factory not really ever having any members.

If I were to pass the dependency through

class Session : ISession
{
    public delegate ISession Factory(string name, Processor processor);

    ...
    public Session(string name, Processor processor)
    {
      ...
    }
}

class TcpServer : ITcpServer
{
    private readonly Session.Factory _sessionFactory;
    private readonly Processor _processor;

    public TcpServer(Session.Factory sessionFactory, Processor processor)
    {
        this._processor = processor;
    }

    ...

    public void OnConnectionReceived()
    {
       ...
       var session = _sessionFactory(ip.LocalEndPoint(), _processor);
       ...

    }
}     

I have two issues with the second approach:

  1. The TcpServer doesn't actually do anything with the Processor. It just passes it along. Seems like this is poor man's DI at work almost.
  2. In the real program behind this code, the Processor actually has a reference to the TcpServer. Therefore when using this approach, I get a circular reference. When I break it apart by using the first scenario then it's not an issue.

What do you think is the best approach? I am open to new ideas as well.

Thanks!

giulianob
  • 178
  • 1
  • 9
  • How are you defining an "abstract factory"? I've been taking your question to be in reference to the [Gang of Four's abstract factory pattern](http://www.dofactory.com/Patterns/PatternAbstract.aspx), but based on your comments, it seems like this may not be what you mean at all. – smartcaveman Nov 13 '11 at 18:04
  • Hi, I have seen the term I am using as Abstract Factory in tons of places. It is basically a delegate/method you call with/without arguments that returns a new instance of an object. It is generally injected into objects that need to create other objects to keep lose coupling. – giulianob Nov 14 '11 at 20:35

2 Answers2

4

Many containers support factories in one or another way and this is the way you should go.

E.g. Taking your example define a ISessionFactory interface like this

public interface ISessionFactory
{
    ISession CreateSession(string name);
}

For Ninject 2.3 see https://github.com/ninject/ninject.extensions.factory and let it be implemented by Ninject

Bind<ISessionFactory>().AsFactory();

For 2.2 do the implementation yourself

public class SessionFactory : ISessionFactory
{
    private IKernel kernel;
    public SessionFactory(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public ISession CreateSession(string name)
    {
        return this.kernel.Get<ISession>(new ConstructorArgument("name", name));
    }
}
Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • But how do you recommend dealing with parameters that should be resolved? Should they be passed into the factory or should the factory do it? – giulianob Nov 13 '11 at 17:20
  • Also is Ninject 2.3 officially released? I haven't upgraded yet since I don't see anything on ninject.org – giulianob Nov 13 '11 at 17:22
  • I don't know hat parameters you want to inject to the factory. The example above is all I would do. And no 2.3 is not released that's why I give 2.2 code as well. The 2.3 example is to show what comes in future. – Remo Gloor Nov 13 '11 at 17:33
  • Let's say that one of the parameters to the Session should be resolved. Would you make your factory take that parameter or would you make it resolve it in the CreateSession function? Edit: Oh.. the Get() would resolve any other dependencies. So you would basically use like what I did but instead of having a delegate you would create an actual class for it. I see. thanks. – giulianob Nov 13 '11 at 17:44
  • Yes exactly. And with the next release it gets even easier as you just have to define the interface and not its implementation. – Remo Gloor Nov 13 '11 at 18:19
  • I looked at the test cases for the factory plugin but did not see any tests where one of the constructor arguments should be resolved. Does it support resolving some arguments while accepting others as parameters? – giulianob Nov 14 '11 at 18:11
1

The pattern I use for an abstract factory pattern is a little different from yours. I use something like setter injection on a generic singleton, but wrap the configurable delegate "property" in a more intuitive interface.

I would prefer not to have to register each implementation individually, so I would prefer to use some convention that can be tested at application start up. I'm not sure about the Ninject syntax for autoregistering custom conventions, but the logic would come down to scanning the relevant assemblies for reference types, T, that have static readonly fields of type AbstractFactory<T>, then calling Configure(Func<T>) on that static member using reflection.

An example of the generic abstract factory singleton and how it would be declared on a Session is below.

 public class Session { 
      public static readonly AbstractFactory<Session> Factory = AbstractFactory<Session>.GetInstance();

 }

 public sealed class AbstractFactory<T> 
     where T: class{

     static AbstractFactory(){
          Bolt = new object();
     }
     private static readonly object Bolt;
     private static AbstractFactory<T> Instance;
     public static AbstractFactory<T> GetInstance(){
          if(Instance == null){
              lock(Bolt){
                  if(Instance == null)
                      Instance = new AbstractFactory<T>();
              }
          }
          return Instance;
     }

     private AbstractFactory(){}

     private Func<T> m_FactoryMethod;

     public void Configure(Func<T> factoryMethod){
              m_FactoryMethod = factoryMethod;
     }

     public T Create() { 
              if(m_FactoryMethod == null) {
                       throw new NotImplementedException();
              }
              return m_FactoryMethod.Invoke();
     } 

 } 

Update

If you need to pass parameters into your factory method, then you can alter the class such as:

 public sealed class AbstractFactory<TDataContract,T> 
      where T: class{
     static AbstractFactory(){
          Bolt = new object();
     }
     private static readonly object Bolt;
     private static AbstractFactory<TDataContract,T> Instance;
     public static AbstractFactory<TDataContract,T> GetInstance(){
          if(Instance == null){
              lock(Bolt){
                  if(Instance == null)
                      Instance = new AbstractFactory<T>();
              }
          }
          return Instance;
     }

     private AbstractFactory(){}

     private Func<TDataContract,T> m_FactoryMethod;

     public void Configure(Func<TDataContract,T> factoryMethod){
              m_FactoryMethod = factoryMethod;
     }

     public T Create(TDataContract data) { 
              if(m_FactoryMethod == null) {
                       throw new NotImplementedException();
              }
              return m_FactoryMethod.Invoke(data);
     } 

 } 

Your SessionData, Session and TcpServer might look like

 public class SessionData{
      public DateTime Start { get; set; }
      public string IpAddress { get; set; }
 }

 public class Session { 
      public static readonly AbstractFactory<SessionData,Session> Factory = AbstractFactory<Session>.GetInstance();

      private readonly string _ip;
      private readonly DateTime _start;

      public Session(SessionData data) { 
           _ip = data.IpAddress;
           _start = DateTime.Now;

      }
      public event EventHandler<RequestReceivedEventEventArgs> RequestAdded;

 }

 public class RequestReceivedEventArgs:  EventArgs { 

     public SessionRequest Request { get; set; }
 }

public class TcpServer : ITcpServer
{
    private readonly Processor _processor;

    public TcpServer(Processor processor)
    {
        this._processor = processor;
    }

    public void OnConnectionReceived()
    {
       var sessionData = new SessionData { 
                                            IpAddress = ip.LocalEndPoint(),
                                            Start = DateTime.Now
                                          };
       var session = Session.Factory.Create(sessionData);

       //...do other stuff
    }

    public void ServeResponse(SessionRequest request){
            _processor.Process(request);
    }
}  

When configuring your DI container, you can set up the factory such as:

Session.Factory.Configure(sessionData => { 
       // instead of injecting the processor into the Session, configure events 
       // that allow the TcpServer to process the data.  
       // (After all, it is more logical for a servers to serve a request than 
       // it is for a Session to do the Processing.  Session's tend to store data
       // and state, not invoke processes
       session.RequestAdded += (sender,e) => { 
              Kernel.Get<ITcpServer>.ServeResponse(e.Request);
       };
});
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
  • How do you pass parameters to the object's constructor though? It seems in this code it is assuming that the factory method knows how to fully create the object but sometimes that is not the case. In my example, the "ip" variable is something that only the TcpServer knows about when a connection comes in. How would your example help with this? – giulianob Nov 13 '11 at 16:20
  • @giulianob, if you need support for input parameters, I see two easy options: (1) you can change the class to `AbstractFactory`, and change the method signatures accordingly, or (2) you can store the required data in some thread static data source, such as `HttpContext.Items` for an ASP.NET app – smartcaveman Nov 13 '11 at 16:27
  • Sorry, I don't think this answers my question. You have shown me a different and useful way of creating abstract factories but my question pertains mainly on how to use a DI container to resolve instances that are required in abstract factories. – giulianob Nov 13 '11 at 16:32
  • @giulianob, Help me try to understand your question. What I am getting is that you are not interested in how to initialize an abstract factory with a DI container, but you are interested in **how to resolve the dependencies of an abstract factory with a DI container**? – smartcaveman Nov 13 '11 at 16:49
  • Yes, assuming that the factory has some parameters that need to be resolved by the DI container and others that should be passed from the object calling the factory. What's the best way to do this? I have posted two ways of doing it: 1) omit the parameters that should be resolved completely, 2) pass the resolved param to the object calling the factory and have it pass to the factory. Not sure which is better or if there's an even better way. – giulianob Nov 13 '11 at 16:52
  • @giulianob, please see updates. I added some additional code, that I think will clarify how you can leverage this approach. Basically, you pass the **data dependencies** to the object via the constructor parameters. If the object has **service dependencies**, then wire them up via events in the abstract factory's factory method. – smartcaveman Nov 13 '11 at 17:17