【问题标题】:Example of Dependency Injection with only base class仅具有基类的依赖注入示例
【发布时间】:2009-09-03 14:25:29
【问题描述】:

是否可以在没有任何第三方工具的情况下进行 DI?在他们发现一些 DI 框架之前,我已经读过有人用抽象类和接口来做这件事。 ID 是如何以这种非常基本的形式完成的?

【问题讨论】:

    标签: .net design-patterns dependency-injection inversion-of-control methodology


    【解决方案1】:

    当你实例化它时,只需将依赖项传递给类的构造函数。当项目很小(几千行代码以下)时,不需要 DI 框架 - 您可以编写一个工厂并手动连接所有依赖项。

    【讨论】:

      【解决方案2】:

      当然,不用第三方工具也是可以的。简单示例:

      interface ILogger
      {
          void Log(string text);
      }
      
      class SomeClass
      {
          private ILogger _logger;
          public SomeClass(ILogger logger)
          {
              _logger = logger;
          }
      
          public void DoSomeWork()
          {
              Log("Starting");
              // do work
      
              Log("Done");
          }
      
          private void Log(string text)
          {
              if (_logger != null)
              {
                  _logger.Log(text);
              }
          }
      }
      

      SomeClassILogger 作为构造函数的输入。它使用它来记录一些输出。假设我们希望它在控制台中:

      class ConsoleLogger : ILogger
      {
          public void Log(string text)
          {
              Console.WriteLine(text);
          }
      }
      

      在一些代码中:

      SomeClass instance = new SomeClass(new ConsoleLogger());
      instance.DoSomeWork();
      

      ..但是我们希望将日志记录在一个文件中:

      class FileLogger : ILogger
      {
          private string _fileName;
          public FileLogger(string fileName)
          {
              _fileName = fileName;
          }
      
          public void Log(string text)
          {
              File.AppendAllText(_fileName, text);
          }
      }
      

      所以,我们改为注入文件记录器:

      SomeClass instance = new SomeClass(new FileLogger("path to file"));
      instance.DoSomeWork();
      

      SomeClass 很高兴不知道正在使用的ILogger 实现,而只是使用注入的任何实现。让工厂创建接口实现的实例而不是在整个代码中构建对象通常是个好主意,这样可以更轻松地更改正在使用的实现。

      【讨论】:

        【解决方案3】:

        this tutorial中有很好的描述。

        基本上你所做的是让DependingClass 只知道Interface,然后用IndependentClass 实现该接口。通过构造函数重载,您可以让单元测试框架发送模拟对象。
        一些代码可能会让我更容易理解我的意思:

        public interface IAnInterface
        {
            void MethodOne();
            void MethodTwo();
        }
        
        public class IndependentClass : IAnInterface
        {
             // Implements all members of IAnInterface
        }
        
        public class DependentClass
        {
            private IAnInterface _dependency;
        
            public DependentClass() : this(new IndependentClass()) { }
        
            public DependentClass(IAnInterface dependency)
            {
                this._dependency = dependency;
            }
        }
        

        现在,如您所见,我们提供了一个默认类类型,如果没有向构造函数提供参数,它将被实例化。但我们也允许注入实现相同接口的不同类,或相同的模拟对象。

        编辑:正如评论中所指出的,在较大的应用程序中,最好有一个实例化DependingClass 的工厂,并删除“默认”构造函数。这样,如果您决定更改实施,您只需在一个地方进行更改。

        【讨论】:

        • 我会质疑默认构造函数是否是个好主意。如果您想要默认构造,最好使用工厂类来获取“默认”实例。这样你就可以将IndependentClass 的实现排除在DependentClass 的实现之外。
        • True - 在更大的应用程序中这是一种更好的方法。然而,对于这个小例子,我发现使用工厂只是为了展示如何在没有第三方框架的情况下完成 DI。
        • 在这种情况下调用默认构造函数有什么好处?您创建的实例从未分配给任何东西。
        • 是的。当调用默认构造函数(我的代码中的第一个)时,构造函数链启动,第二个构造函数被调用,参数为new IndependentClass()。在第二个构造函数的方法体中,IndependentClass 的实例被赋值给了字段。
        【解决方案4】:

        您可以创建通过接口相互通信的组件,并让您的托管程序实例化组件并将它们链接在一起。

        这将是您的解决方案结构:

        • 一个 dll 程序集,用于定义组件之间的契约(接口 + 数据对象,它们是接口方法签名的一部分)。

        • 一个或多个定义组件(实现接口)的 dll 程序集。组件之间的任何通信都是通过接口完成的。

        • 一个 exe 程序集,用于启动宿主进程、实例化组件并链接它们以设置一些属性。每当您需要替换一个组件时,您只需更改此项目即可。

        您可以为任何组件创建单元测试,模拟您正在测试的组件所使用的组件。

        您还可以从托管项目的 app.confing 文件中读取属性绑定。

        【讨论】:

        • 谢谢。我想这就是为什么我看不到 DI 的必要性,因为我很少使用接口并且仍然实现松散耦合的类/组件。
        【解决方案5】:

        您可以通过三种方式做到这一点...

        1. 在构造函数中传递对依赖实例(当然是实现接口的类的实例)的引用。

          public class MyClass
          {
              private readonly ISpillDAL iSpDal;
              public ISpillDAL SpillDal { get { return iSpDal; } }
              public SpillLogic() : this(null) { }
              public SpillLogic(ISpillDAL splDAL)
              {
                 iSpDal = splDAL ?? new SpillDAL();  // this allows a default 
              }
          }
          
        2. 创建新对象并然后通过属性设置器将引用传递给依赖对象

          public class MyClass 
          { 
             private readonly ISpillDAL iSpDal; 
             public ISpillDAL SpillDal    
             { 
                set { iSpDal = value; } 
                get { return iSpDal; } 
             }
             public SpillLogic() { }        
          }
          
        3. 在对象中使用一个函数来接受引用并将其分配给您为此创建的内部 provbate 变量

          public class MyClass 
          { 
             private readonly ISpillDAL iSpDal; 
             public ISpillDAL SpillDal    
             { 
                set { iSpDal = value; } 
                get { return iSpDal; } 
             }
             public SpillLogic() { }    
             public void InjectSpillDAL(  ISpillDAL splDAL )
             {  iSpDal = splDAL; } 
          }
          

        【讨论】:

        • 所以 DI 只对接口有用,对吧?如果您有上述场景但没有接口而不是使用 DI?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多