使用依赖注入框架时的抽象工厂

时间:2011-11-13 15:16:18

标签: c# dependency-injection inversion-of-control

我想知道如何在使用DI框架时正确使用抽象工厂,并且该工厂中的一个参数是应该由DI框架处理的依赖项。

我不确定是否要让我的抽象工厂完全省略参数然后使用我的DI容器来连接它或者是否应该将依赖项传递给对象。

例如,我有一个TcpServer,它使用Session.Factory来创建套接字。 Session对象实际上在其构造函数中使用了Processor。我应该将处理器传递给TcpServer然后将它传递给Session.Factory还是让我的DI容器进行接线?

如果我要让DI容器进行接线,它将如下所示:

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());
       ...

    }
}

然后使用像Ninject这样的DI容器,我可以在配置容器时执行此操作:

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

我使用这种方法的主要问题是它假设创建Session.Factory的人知道处理器。在我的情况下,由于我使用的是DI容器,这实际上非常方便,但是让工厂拥有自己的依赖项似乎很奇怪。我一直想象一个工厂真的没有任何成员。

如果我通过

传递依赖关系
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);
       ...

    }
}     

我对第二种方法有两个问题:

  1. TcpServer实际上对处理器没有任何作用。它只是传递它。这似乎是穷人的DI几乎在工作。
  2. 在此代码背后的实际程序中,Processor实际上有一个对TcpServer的引用。因此,当使用这种方法时,我得到一个循环引用。当我通过使用第一个场景将其分开时,那不是问题。
  3. 您认为最好的方法是什么?我也对新想法持开放态度。

    谢谢!

2 个答案:

答案 0 :(得分:4)

许多容器以一种或另一种方式支持工厂,这就是你应该去的方式。

E.g。以您的示例为例,定义一个ISessionFactory接口

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

对于Ninject 2.3,请参阅https://github.com/ninject/ninject.extensions.factory并让它由Ninject实施

Bind<ISessionFactory>().AsFactory();

对于2.2自己实施

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));
    }
}

答案 1 :(得分:1)

我用于抽象工厂模式的模式与您的模式略有不同。我在通用单例上使用类似setter注入的东西,但在更直观的界面中包装可配置委托“property”。

我宁愿不必单独注册每个实现,所以我更喜欢使用一些可以在应用程序启动时测试的约定。我不确定用于自动调整自定义约定的Ninject语法,但逻辑将归结为扫描相关程序集的引用类型T,它们具有AbstractFactory<T>类型的静态只读字段,然后调用{{1}使用反射在静态成员上。

通用抽象工厂单例的一个示例以及如何在Configure(Func<T>)上声明它。

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();
     } 

 } 

您的 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); } } SessionDataSession可能看起来像

TcpServer

配置DI容器时,可以设置工厂,例如:

 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);
    }
}