Abstract plants using dependency injection frameworks

I am wondering how to properly use abstract factories when using the DI infrastructure, and one of the parameters in this factory is the dependency that should be handled by the DI framework.

I am not sure what to do my abstract factory to completely omit the parameter, and then use my DI container to connect it, or should I pass an object dependency.

For example, I have TcpServer and it uses Session.Factory to create sockets. The Session object actually accepts the processor in its constructor. Should I transfer the Processor to TcpServer, then transfer it to Session.Factory or will my DI container post?

If I had a DI container, the wiring would be 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 could do this when setting up the container:

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

My main problem with this approach is that it assumes that anyone who creates Session.Factory knows about the processor. In my case, since I use a DI container, it is actually very convenient, but it seems strange to have a factory of its own dependencies. I always imagined that the factory does not have any members.

If I had 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 questions with a second approach:

  • TcpServer does virtually nothing with the processor. He just passes it on. It seems like this is a bad DI person at work almost.
  • In a real program of this code, the processor actually has a link to TcpServer. Therefore, when using this approach, I get a circular link. When I break it using the first script, this is not a problem.

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

Thanks!

+4
source share
2 answers

Many containers support factories in one way or another, and you should go that way.

eg. Taking your example, define an 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)); } } 
+4
source

The sample I use for the abstract factory template is slightly different from yours. I use something like setter injection on a shared singlet, but I wrap the delegate's custom "property" in a more intuitive interface.

I would prefer not to register each implementation separately, so I would prefer to use some kind of convention that can be tested when the application starts. I'm not sure about the Ninject syntax for automatically registering user agreements, but the logic boils down to checking the appropriate assemblies for T reference types that have readonly static fields of type AbstractFactory<T> , and then calling Configure(Func<T>) on this static member using reflection.

The following is an example of a general abstract factory syntax and how it will be declared at Session .

  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 to your factory method, you can change the class, for example:

  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 may 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 setting up a DI container, you can set up a factory, for example:

 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 tend to store data // and state, not invoke processes session.RequestAdded += (sender,e) => { Kernel.Get<ITcpServer>.ServeResponse(e.Request); }; }); 
+1
source

Source: https://habr.com/ru/post/1381001/


All Articles