代码之家  ›  专栏  ›  技术社区  ›  terjetyl

依赖注入替代方案

  •  14
  • terjetyl  · 技术社区  · 16 年前

    public class BusinessProducts
    {
       IDataContext _dx;
    
       BusinessProducts(IDataContext dx)
       {
          _dx = dx;
       }
    
       public List<Product> GetProducts()
       {
        return dx.GetProducts();
       }
    }
    

    BusinessProducts bp = new BusinessProducts(dataContextImplementation);
    

    我会继续写作

    BusinessProducts bp = new BusinessProducts();
    

    因为我觉得第一种选择很不现实。我不想知道商业产品“依赖”什么来获得产品,而且我觉得它使我的代码更难阅读。

    我正在用c#编写代码,但欢迎其他语言的替代方案

    11 回复  |  直到 16 年前
        1
  •  9
  •   tvanfosson    15 年前

    我使用一个工厂作为我的上下文并注入它,如果提供的工厂为空,则提供一个合适的默认值。我这样做有两个原因。首先,我使用数据上下文作为工作范围对象的一个单元,因此我需要能够在需要时创建它们,而不是保留它们。其次,我主要使用DI来提高可测试性,而解耦只是次要考虑因素。

    因此,我的业务产品类将如下所示:

    public class BusinessProducts
    {
         private IDataContextFactory DataContextFactory { get; set; }  // my interface
    
         public BusinessProducts() : this(null) {}
    
         public BusinessProducts( IDataContextFactory factory )
         {
              this.DataContext = factory ?? new BusinessProductsDataContextFactory();
         }
    
         public void DoSomething()
         {
              using (DataContext dc = this.DataContextFactory().CreateDataContext())
              {
                 ...
              }
         }
    

    另一种方法是使factory属性可公开设置,并通过设置该属性注入备用工厂。无论哪种方式,如果您想要保留空构造函数,您都需要提供一个默认值。

        2
  •  6
  •   troelskn    16 年前

    您可以创建一个工厂。DI容器最适合于在安装时进行的布线,而不是在运行时(因为这看起来是一个示例)。工厂可以以不同的方式实现,这取决于它需要的可插拔性以及需要使用它的位置。

        3
  •  6
  •   pkaeding    16 年前

    我通常会有一个空构造函数,它使用一个实体实例(或由IoC创建的实例),而不是一个带有DI的实例。即

    public class BusinessProducts
    {
       IDataContext _dx;
    
       BusinessProducts()
       {
          _dx = new SolidDataContext();
       }
    
       BusinessProducts(IDataContext dx)
       {
          _dx = dx;
       }
    }
    

    通过这种方式,您可以在单元测试中使用DI覆盖默认实例。

        4
  •  4
  •   Bryan Watts    16 年前

    这个 Dependency Injection 模式是 Inversion of Control

    这意味着,与您的类控制它使用的其他类的实例不同,这种关系是 并为其提供了依赖项。

        5
  •  3
  •   krosenvold    16 年前

    这里有两种不同的情况:

    在生产代码中,您将 从不

    new BusinessProducts(dataContextImplementation)
    

    因为依赖注入通常会为您创建完整的对象层次结构。这是依赖注入模式的“病毒式”本质,它们倾向于完全控制您的服务创建。

        6
  •  1
  •   Strelok    16 年前

    http://springframework.net/ http://structuremap.sourceforge.net/Default.htm 可能是基于.NET语言最常用的DI框架,它们都可以满足您的需要。

        7
  •  1
  •   Szymon Rozga    16 年前

    通常,框架本身具有构建整个对象树的逻辑。例如,代替

    new SomeObjectO(diContext)
    

    您可以这样称呼该框架:

    DIFramework.GetNew<SomeObjectO>();
    

    DIFramework.Get<SomeObject>();
    

    如果您想了解DI及其过程,另一个有趣的框架是Microsoft的Unity和Object Builder项目。

        8
  •  1
  •   hangy    16 年前

    CommonServiceLocator 使用您最喜欢的兼容.NET依赖注入框架。这将允许您编写如下代码:

    public class BusinessProducts
    {
       IDataContext _dx;
    
       BusinessProducts()
       {
          _dx = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IDataContext>();
       }
    
       public List<Product> GetProducts()
       {
        return dx.GetProducts();
       }
    }
    

    但是,请注意,当大多数人知道您使用依赖项注入框架时,这并不是他们所期望的。我认为使用依赖注入框架并让它为您创建所有对象更为常见。

    BusinessProducts bp = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<BusinessProducts>();
    

    如果您希望避免依赖注入框架路径,那么使用工厂可能是最好的方法。

        9
  •  1
  •   Scott Cowan    16 年前

    public class BusinessProducts
    {
       IDataContext _dx;
    
       BusinessProducts() : this(new DataContext()) {}
    
       BusinessProducts(IDataContext dx)
       {
          _dx = dx;
       }
    
       public List<Product> GetProducts()
       {
        return dx.GetProducts();
       }
    }
    

    这并不理想,因为它将您与实现联系在一起,但它是实现解耦代码的良好垫脚石。这类似于@tvanfosson,但要简单得多。

        10
  •  1
  •   Chris Marisic    16 年前

    我的代码将引用MicrosoftUnity,但我确信它非常适用于所有DI框架。如果您正确地使用DI,您永远不需要调用新的BusinessObject(新的dataContext),DI关联将为您处理这一切。

    我的示例将有点长,因为我将粘贴一些代码,用于运行由Unity完全DI加载的Model View Presenter网站。(如果您想要完整的源代码,请查看我的博客并从我的Assembla SVN服务器下载)

    protected void Application_Start(object sender, EventArgs e)
    {
        Application.GetContainer()
            // presenters / controllers are per request                 
            .RegisterType<IEmployeeController, EmployeeController>(new ContextLifetimeManager<IEmployeeController>())
    
            //Data Providers are Per session                
            .RegisterType<IEmployeeDataProvider, EmployeeDataProvider>(new SessionLifetimeManager<IEmployeeDataProvider>())
    
            //Session Factory is life time
            .RegisterType<INHibernateSessionManager, NHibernateSessionManager>(new ContainerControlledLifetimeManager());
    }
    

    自定义HTTP模块在OnPreRequest调用期间为每个页面调用Unity Building方法。

    private static void OnPreRequestHandlerExecute(object sender, EventArgs e)
    {
        var handler = HttpContext.Current.Handler;
        HttpContext.Current.Application.GetContainer().BuildUp(handler.GetType(), handler);
    
        // User Controls are ready to be built up after the page initialization is complete
        var page = HttpContext.Current.Handler as Page;
        if (page != null)
        {
            page.InitComplete += OnPageInitComplete;
        }
    }
    

    public partial class Employees : Page, IEmployeeView
    {
        private EmployeePresenter _presenter;
    
        [Dependency]
        public EmployeePresenter Presenter
        {
            set
            {
                _presenter = value;
                _presenter.View = this;
            }
        }
    }
    

    具有InjectionConstructor方法的演示者

    public class EmployeePresenter : Presenter<IEmployeeView>
    {
        private readonly IEmployeeController _controller;
    
        [InjectionConstructor]
        }
        public EmployeePresenter(IEmployeeController controller)
        {
            _controller = controller;
    }
    

    控制员也这样做

    public class EmployeeController : IEmployeeController
    {
        private readonly IEmployeeDataProvider _provider;
    
        [InjectionConstructor]
        public EmployeeController(IEmployeeDataProvider DataProvider)
        {
            _provider = DataProvider;
        }
    }
    

    与提供商相同

    公共类EmployeeController:IEEmployeeController
    {
    私有只读IEEmployeeDataProvider\u provider;
    
    [注入构造函数]
    {
    _提供者=数据提供者;
    }
    }
    

    public class NHibernateSessionManager : INHibernateSessionManager
    {   
        private readonly ISessionFactory _sessionFactory;
    
        public NHibernateSessionManager()
        {            
            _sessionFactory = GetSessionFactory();
        }
    }
    

    因此,当页面请求启动时,HttpModule会在页面上调用build()方法。Unity然后会看到标记了Dependency属性的属性,并会检查它的容器,看看里面是否存在EmployeePresenter对象。

    因为容器中没有这样的对象,所以它将尝试创建EmployeePresenter。在检查创建它在Presenter中看到的类时,它需要一个构造函数,该构造函数需要将IEmployeeController注入其中。由于容器实际上有一个控制器的管理器,它将看到它的实例是否存在于页面请求开头不存在的容器中,因此它将去实例化控制器。

    Unity随后将看到控制器需要向其中注入IEmployeDataProvider,并且它将继续此过程,直到最终到达提供者需要注入会话管理器的位置。由于会话管理器不再需要注入,Unity将创建会话管理器的一个实例,并将其存储在给定的ContainerLiveTimeManager的容器中,将其注入提供程序并存储该实例,依此类推,直到完成为页面创建EmployeePresenter依赖关系为止。

        11
  •  0
  •   sirrocco    16 年前

    你也可以看看 windsor