使用dependency injection框架时的抽象工厂

我想知道如何在使用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().ToMethod(c => { var processor = Kernel.Get(); 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的引用。 因此,当使用这种方法时,我得到一个循环引用。 当我使用第一个场景将其分开时,那不是问题。

您认为最好的方法是什么? 我也对新想法持开放态度。

谢谢!

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

举例来说,像这样定义一个ISessionFactory接口

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

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

 Bind().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(new ConstructorArgument("name", name)); } } 

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

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

下面是一个通用抽象工厂单例的示例以及如何在Session上声明它。

  public class Session { public static readonly AbstractFactory Factory = AbstractFactory.GetInstance(); } public sealed class AbstractFactory where T: class{ static AbstractFactory(){ Bolt = new object(); } private static readonly object Bolt; private static AbstractFactory Instance; public static AbstractFactory GetInstance(){ if(Instance == null){ lock(Bolt){ if(Instance == null) Instance = new AbstractFactory(); } } return Instance; } private AbstractFactory(){} private Func m_FactoryMethod; public void Configure(Func factoryMethod){ m_FactoryMethod = factoryMethod; } public T Create() { if(m_FactoryMethod == null) { throw new NotImplementedException(); } return m_FactoryMethod.Invoke(); } } 

更新

如果需要将参数传递给工厂方法,则可以更改类,例如:

  public sealed class AbstractFactory where T: class{ static AbstractFactory(){ Bolt = new object(); } private static readonly object Bolt; private static AbstractFactory Instance; public static AbstractFactory GetInstance(){ if(Instance == null){ lock(Bolt){ if(Instance == null) Instance = new AbstractFactory(); } } return Instance; } private AbstractFactory(){} private Func m_FactoryMethod; public void Configure(Func factoryMethod){ m_FactoryMethod = factoryMethod; } public T Create(TDataContract data) { if(m_FactoryMethod == null) { throw new NotImplementedException(); } return m_FactoryMethod.Invoke(data); } } 

您的SessionDataSessionTcpServer可能看起来像

  public class SessionData{ public DateTime Start { get; set; } public string IpAddress { get; set; } } public class Session { public static readonly AbstractFactory Factory = AbstractFactory.GetInstance(); private readonly string _ip; private readonly DateTime _start; public Session(SessionData data) { _ip = data.IpAddress; _start = DateTime.Now; } public event EventHandler 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); } } 

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

 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.ServeResponse(e.Request); }; });