什么是依赖注入?

发布于 2024-07-05 21:56:12 字数 180 浏览 6 评论 0原文

已经发布了几个关于依赖注入的具体问题,例如何时使用它以及有哪些框架。 但是,

什么是依赖注入以及何时/为什么应该或不应该使用它?

There have been several questions already posted with specific questions about dependency injection, such as when to use it and what frameworks are there for it. However,

What is dependency injection and when/why should or shouldn't it be used?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

心安伴我暖 2024-07-12 21:56:12

我知道已经有很多答案,但我发现这非常有帮助: http://tutorials .jenkov.com/dependency-injection/index.html

无依赖关系:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

依赖关系:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

注意如何将 DataSourceImpl 实例化移动到构造函数中。 构造函数采用四个参数,即 DataSourceImpl 所需的四个值。 尽管 MyDao 类仍然依赖于这四个值,但它本身不再满足这些依赖关系。 它们由创建 MyDao 实例的任何类提供。

I know there are already many answers, but I found this very helpful: http://tutorials.jenkov.com/dependency-injection/index.html

No Dependency:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Dependency:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Notice how the DataSourceImpl instantiation is moved into a constructor. The constructor takes four parameters which are the four values needed by the DataSourceImpl. Though the MyDao class still depends on these four values, it no longer satisfies these dependencies itself. They are provided by whatever class creating a MyDao instance.

樱花细雨 2024-07-12 21:56:12

摘自Apress.Spring.Persistence.with.Hibernate.Oct.2010
一书

依赖注入的目的是解耦
解决应用程序业务中的外部软件组件问题
逻辑。没有依赖注入,组件如何的细节
访问所需的服务可能会与组件的混乱
代码。 这不仅增加了出错的可能性,还增加了代码
臃肿,并增加维护复杂性; 它耦合组件
更紧密地结合在一起,使得修改依赖关系变得困难
重构或测试。

from Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

The purpose of dependency injection is to decouple the work of
resolving external software components from your application business
logic.Without dependency injection, the details of how a component
accesses required services can get muddled in with the component’s
code. This not only increases the potential for errors, adds code
bloat, and magnifies maintenance complexities; it couples components
together more closely, making it difficult to modify dependencies when
refactoring or testing.

灵芸 2024-07-12 21:56:12

到目前为止,我找到的最好的定义是 James Shore 的定义

“依赖注入”是 25 美元
5 美分概念的术语。 [...]
依赖注入意味着给予
对象及其实例变量。 [...]。

Martin Fowler 的一篇文章也可能有用。

依赖注入基本上是提供对象所需的对象(其依赖项),而不是让它自己构造它们。 这是一种非常有用的测试技术,因为它允许模拟或删除依赖项。

可以通过多种方式将依赖项注入到对象中(例如构造函数注入或 setter 注入)。 人们甚至可以使用专门的依赖注入框架(例如Spring)来做到这一点,但它们当然不是必需的。 您不需要这些框架来进行依赖项注入。 显式实例化和传递对象(依赖项)与框架注入一样好。

The best definition I've found so far is one by James Shore:

"Dependency Injection" is a 25-dollar
term for a 5-cent concept. [...]
Dependency injection means giving an
object its instance variables. [...].

There is an article by Martin Fowler that may prove useful, too.

Dependency injection is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself. It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out.

Dependencies can be injected into objects by many means (such as constructor injection or setter injection). One can even use specialized dependency injection frameworks (e.g. Spring) to do that, but they certainly aren't required. You don't need those frameworks to have dependency injection. Instantiating and passing objects (dependencies) explicitly is just as good an injection as injection by framework.

浪菊怪哟 2024-07-12 21:56:12

依赖注入是将依赖传递给其他对象框架(依赖注入器)。

依赖注入使测试变得更加容易。 注入可以通过构造函数完成。

SomeClass() 的构造函数如下:

public SomeClass() {
    myObject = Factory.getObject();
}

问题
如果myObject涉及磁盘访问或网络访问等复杂任务,则对SomeClass()进行单元测试是困难的。 程序员必须模拟 myObject 并且可能拦截工厂调用。

替代解决方案

  • myObject 作为参数传递给构造函数
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject 可以直接传递,这使得测试更容易。

  • 一种常见的替代方法是定义一个不执行任何操作的构造函数。 依赖注入可以通过setter来完成。 (h/t@MikeVella)。
  • Martin Fowler 记录了第三种替代方案 (h/t @MarcDix),其中类为程序员希望注入的依赖项显式实现接口。

如果没有依赖注入,在单元测试中隔离组件会更加困难。

2013 年,当我写下这个答案时,这是 Google 测试博客 上的一个主要主题。 这对我来说仍然是最大的优势,因为程序员并不总是需要在运行时设计中具有额外的灵活性(例如,服务定位器或类似的模式)。 程序员经常需要在测试期间隔离类。

Dependency Injection is passing dependency to other objects or framework( dependency injector).

Dependency injection makes testing easier. The injection can be done through constructor.

SomeClass() has its constructor as following:

public SomeClass() {
    myObject = Factory.getObject();
}

Problem:
If myObject involves complex tasks such as disk access or network access, it is hard to do unit test on SomeClass(). Programmers have to mock myObject and might intercept the factory call.

Alternative solution:

  • Passing myObject in as an argument to the constructor
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject can be passed directly which makes testing easier.

  • One common alternative is defining a do-nothing constructor. Dependency injection can be done through setters. (h/t @MikeVella).
  • Martin Fowler documents a third alternative (h/t @MarcDix), where classes explicitly implement an interface for the dependencies programmers wish injected.

It is harder to isolate components in unit testing without dependency injection.

In 2013, when I wrote this answer, this was a major theme on the Google Testing Blog. It remains the biggest advantage to me, as programmers not always need the extra flexibility in their run-time design (for instance, for service locator or similar patterns). Programmers often need to isolate the classes during testing.

小伙你站住 2024-07-12 21:56:12

我在松散耦合方面发现了这个有趣的例子:

来源:< em>了解依赖注入

任何应用程序由许多对象组成,这些对象相互协作以执行一些有用的操作。 传统上,每个对象负责获取对其协作的依赖对象(依赖项)的自己的引用。 这导致了高度耦合的类和难以测试的代码。

例如,考虑一个 Car 对象。

汽车依靠车轮、发动机、燃料、电池等来运行。 传统上,我们定义此类依赖对象的品牌以及 Car 对象的定义。

没有依赖注入 (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

这里,Car 对象负责创建依赖对象。

如果我们想要更改对象的类型该怎么办?它的依赖对象 - 比如说 Wheel - 在最初的 NepaliRubberWheel() 刺穿之后?
我们需要使用新的依赖项(如 ChineseRubberWheel())重新创建 Car 对象,但只有 Car 制造商可以做到这一点。

那么依赖注入为我们做了什么......?

当使用依赖注入时,对象在运行时被赋予它们的依赖关系而不是编译时间(汽车制造时间)
这样我们现在就可以随时更改Wheel。 在这里,可以在运行时将依赖项 (wheel)注入到Car中。

使用依赖项注入后:

在这里,我们在运行时注入依赖项(车轮和电池)。 因此出现了术语:依赖注入。我们通常依赖 Spring、Guice、Weld 等 DI 框架来创建依赖项并在需要时进行注入。

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

优点是:

  • 解耦对象的创建(换句话说,将使用与对象的创建分开)
  • 能够替换依赖项(例如:Wheel、Battery),而无需更改使用它的类(Car)
  • 促进“代码到接口而不是实现”原则
  • 在测试期间创建和使用模拟依赖项的能力(如果我们想在测试期间使用模拟轮而不是真实实例..我们可以创建模拟轮对象并让 DI 框架注入车)

I found this funny example in terms of loose coupling:

Source: Understanding dependency injection

Any application is composed of many objects that collaborate with each other to perform some useful stuff. Traditionally each object is responsible for obtaining its own references to the dependent objects (dependencies) it collaborate with. This leads to highly coupled classes and hard-to-test code.

For example, consider a Car object.

A Car depends on wheels, engine, fuel, battery, etc. to run. Traditionally we define the brand of such dependent objects along with the definition of the Car object.

Without Dependency Injection (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Here, the Car object is responsible for creating the dependent objects.

What if we want to change the type of its dependent object - say Wheel - after the initial NepaliRubberWheel() punctures?
We need to recreate the Car object with its new dependency say ChineseRubberWheel(), but only the Car manufacturer can do that.

Then what does the Dependency Injection do for us...?

When using dependency injection, objects are given their dependencies at run time rather than compile time (car manufacturing time).
So that we can now change the Wheel whenever we want. Here, the dependency (wheel) can be injected into Car at run time.

After using dependency injection:

Here, we are injecting the dependencies (Wheel and Battery) at runtime. Hence the term : Dependency Injection. We normally rely on DI frameworks such as Spring, Guice, Weld to create the dependencies and inject where needed.

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

The advantages are:

  • decoupling the creation of object (in other word, separate usage from the creation of object)
  • ability to replace dependencies (eg: Wheel, Battery) without changing the class that uses it(Car)
  • promotes "Code to interface not to implementation" principle
  • ability to create and use mock dependency during test (if we want to use a Mock of Wheel during test instead of a real instance.. we can create Mock Wheel object and let DI framework inject to Car)
九局 2024-07-12 21:56:12

依赖注入是一种实践,其中对象的设计方式是从其他代码段接收对象的实例,而不是在内部构造它们。 这意味着可以在不更改代码的情况下替换实现该对象所需接口的任何对象,从而简化了测试并提高了解耦性。

例如,考虑以下类:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

在此示例中,PersonService::addManagerPersonService::removeManager 的实现需要 GroupMembershipService 的实例> 以便完成其工作。 如果没有依赖注入,传统的方法是在 PersonService 的构造函数中实例化一个新的 GroupMembershipService ,并在两个函数中使用该实例属性。 但是,如果 GroupMembershipService 的构造函数需要多个东西,或者更糟糕的是,需要在 GroupMembershipService 上调用一些初始化“setter”,那么代码就会增长速度相当快,PersonService 现在不仅依赖于 GroupMembershipService,还依赖于 GroupMembershipService 所依赖的其他所有内容。 此外,与 GroupMembershipService 的链接被硬编码到 PersonService 中,这意味着您无法出于测试目的“虚拟”GroupMembershipService,或者在应用程序的不同部分使用策略模式。

通过依赖注入,您可以将其传递给 PersonService 构造函数,或者添加一个属性(getter 和 setter)来设置它的本地实例。 这意味着您的 PersonService 不再需要担心如何创建 GroupMembershipService,它只接受给定的那些,并与它们一起工作。 这也意味着 GroupMembershipService 的子类或实现 GroupMembershipService 接口的任何内容都可以“注入”到 PersonService 中,并且 < code>PersonService 不需要知道更改。

Dependency Injection is a practice where objects are designed in a manner where they receive instances of the objects from other pieces of code, instead of constructing them internally. This means that any object implementing the interface which is required by the object can be substituted in without changing the code, which simplifies testing, and improves decoupling.

For example, consider these clases:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

In this example, the implementation of PersonService::addManager and PersonService::removeManager would need an instance of the GroupMembershipService in order to do its work. Without Dependency Injection, the traditional way of doing this would be to instantiate a new GroupMembershipService in the constructor of PersonService and use that instance attribute in both functions. However, if the constructor of GroupMembershipService has multiple things it requires, or worse yet, there are some initialization "setters" that need to be called on the GroupMembershipService, the code grows rather quickly, and the PersonService now depends not only on the GroupMembershipService but also everything else that GroupMembershipService depends on. Furthermore, the linkage to GroupMembershipService is hardcoded into the PersonService which means that you can't "dummy up" a GroupMembershipService for testing purposes, or to use a strategy pattern in different parts of your application.

With Dependency Injection, instead of instantiating the GroupMembershipService within your PersonService, you'd either pass it in to the PersonService constructor, or else add a Property (getter and setter) to set a local instance of it. This means that your PersonService no longer has to worry about how to create a GroupMembershipService, it just accepts the ones it's given, and works with them. This also means that anything which is a subclass of GroupMembershipService, or implements the GroupMembershipService interface can be "injected" into the PersonService, and the PersonService doesn't need to know about the change.

小猫一只 2024-07-12 21:56:12

让我们尝试一下 CarEngine 类的简单示例,任何汽车都需要引擎才能到达任何地方,至少目前是这样。 下面是在没有依赖注入的情况下代码的外观。

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

为了实例化 Car 类,我们将使用下一个代码:

Car car = new Car();

此代码的问题是我们与 GasEngine 紧密耦合,如果我们决定将其更改为 ElectricityEngine,那么我们将需要重写 Car 类。 应用程序越大,我们将不得不添加和使用新型引擎的问题和头痛就越多。

换句话说,通过这种方法,我们的高级 Car 类依赖于较低级别的 GasEngine 类,这违反了 SOLID 的依赖倒置原则 (DIP)。 DIP 建议我们应该依赖抽象,而不是具体的类。 因此,为了满足这一点,我们引入了 IEngine 接口并重写了如下代码:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

现在我们的 Car 类仅依赖于 IEngine 接口,而不依赖于引擎的具体实现。
现在,唯一的技巧是我们如何创建 Car 的实例并为其提供一个实际的具体 Engine 类,如 GasEngine 或 ElectricityEngine。 这就是依赖注入的用武之地。

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

这里我们基本上将我们的依赖项(Engine 实例)注入(传递)到 Car 构造函数。 因此,现在我们的类在对象及其依赖项之间具有松散耦合,我们可以轻松添加新类型的引擎,而无需更改 Car 类。

依赖注入的主要好处是类的耦合更加松散,因为它们没有硬编码的依赖关系。 这遵循上面提到的依赖倒置原则。 类不是引用特定的实现,而是请求抽象(通常是接口),这些抽象在构造类时提供给它们。

所以最终依赖注入只是一种技术
实现对象及其依赖关系之间的松耦合。
而不是直接实例化类所需的依赖项
为了执行其操作,向类提供依赖项
(最常见的是)通过构造函数注入。

此外,当我们有很多依赖项时,使用控制反转(IoC)容器是非常好的做法,我们可以知道哪些接口应该映射到所有依赖项的哪些具体实现,并且我们可以让它在构造时为我们解析这些依赖项我们的对象。 例如,我们可以在 IoC 容器的映射中指定 IEngine 依赖项应映射到 GasEngine 类,并且当我们向 IoC 容器请求我们的实例时Car 类,它将自动构造我们的 Car 类,并传入 GasEngine 依赖项。

更新: 观看了有关的课程Julie Lerman 最近提供的 EF Core,也喜欢她关于 DI 的简短定义。

依赖注入是一种允许应用程序注入的模式
动态地将对象传递给需要它们的类,而不强制这些对象
负责这些对象的类。 它允许你的代码是
耦合更加松散,Entity Framework Core 也可以插入其中
服务体系。

Let's try simple example with Car and Engine classes, any car need an engine to go anywhere, at least for now. So below how code will look without dependency injection.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

And to instantiate the Car class we will use next code:

Car car = new Car();

The issue with this code that we tightly coupled to GasEngine and if we decide to change it to ElectricityEngine then we will need to rewrite Car class. And the bigger the application the more issues and headache we will have to add and use new type of engine.

In other words with this approach is that our high level Car class is dependent on the lower level GasEngine class which violate Dependency Inversion Principle(DIP) from SOLID. DIP suggests that we should depend on abstractions, not concrete classes. So to satisfy this we introduce IEngine interface and rewrite code like below:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

Now our Car class is dependent on only the IEngine interface, not a specific implementation of engine.
Now, the only trick is how do we create an instance of the Car and give it an actual concrete Engine class like GasEngine or ElectricityEngine. That's where Dependency Injection comes in.

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Here we basically inject(pass) our dependency(Engine instance) to Car constructor. So now our classes have loose coupling between objects and their dependencies, and we can easily add new types of engines without changing the Car class.

The main benefit of the Dependency Injection that classes are more loosely coupled, because they do not have hard-coded dependencies. This follows the Dependency Inversion Principle, which was mentioned above. Instead of referencing specific implementations, classes request abstractions (usually interfaces) which are provided to them when the class is constructed.

So in the end Dependency injection is just a technique for
achieving loose coupling between objects and their dependencies.
Rather than directly instantiating dependencies that class needs in
order to perform its actions, dependencies are provided to the class
(most often) via constructor injection.

Also when we have many dependencies it is very good practice to use Inversion of Control(IoC) containers which we can tell which interfaces should be mapped to which concrete implementations for all our dependencies and we can have it resolve those dependencies for us when it constructs our object. For example, we could specify in the mapping for the IoC container that the IEngine dependency should be mapped to the GasEngine class and when we ask the IoC container for an instance of our Car class, it will automatically construct our Car class with a GasEngine dependency passed in.

UPDATE: Watched course about EF Core from Julie Lerman recently and also liked her short definition about DI.

Dependency injection is a pattern to allow your application to inject
objects on the fly to classes that need them, without forcing those
classes to be responsible for those objects. It allows your code to be
more loosely coupled, and Entity Framework Core plugs in to this same
system of services.

晌融 2024-07-12 21:56:12

接受的答案是一个很好的答案 - 但我想补充一点,DI 非常类似于在代码中避免硬编码常量的经典做法。

当您使用一些常量(例如数据库名称)时,您可以快速将其从代码内部移动到某个配置文件,并将包含该值的变量传递到需要的地方。 这样做的原因是这些常量通常比代码的其余部分更频繁地更改。 例如,如果您想在测试数据库中测试代码。

DI 与面向对象编程领域中的情况类似。 那里的值而不是常量文字是整个对象 - 但将创建它们的代码从类代码中移出的原因是相似的 - 对象的更改比使用它们的代码更频繁。 需要进行此类更改的一个重要情况是测试。

The accepted answer is a good one - but I would like to add to this that DI is very much like the classic avoiding of hardcoded constants in the code.

When you use some constant like a database name you'd quickly move it from the inside of the code to some config file and pass a variable containing that value to the place where it is needed. The reason to do that is that these constants usually change more frequently than the rest of the code. For example if you'd like to test the code in a test database.

DI is analogous to this in the world of Object Oriented programming. The values there instead of constant literals are whole objects - but the reason to move the code creating them out from the class code is similar - the objects change more frequently then the code that uses them. One important case where such a change is needed is tests.

枉心 2024-07-12 21:56:12

在进行技术描述之前,首先用一个现实生活中的例子来形象化它,因为你会发现很多技术东西来学习依赖注入,但大多数人无法理解它的核心概念。

在第一张图片中,假设您有一个拥有大量车辆的汽车工厂。 汽车实际上是在装配单元中制造出来的,但它需要发动机座椅以及车轮。 因此,装配单元依赖于所有这些单元,并且它们是工厂的依赖性

你会觉得现在维护这个工厂的所有任务太复杂了,因为除了主要任务(在装配单元组装汽车)之外,你还必须关注其他单元。 现在维护成本非常高,而且厂房很大,所以需要额外的租金。

现在,看第二张图。 如果您发现一些供应商公司可以为您提供车轮座椅发动机,并且价格比您自己生产的成本更便宜,那么现在您不需要在你的工厂制造它们。 您现在可以为您的装配单元租用较小的建筑物,这将减少您的维护任务并减少额外的租赁成本。 现在您也可以只专注于您的主要任务(汽车组装)。

现在我们可以说组装汽车的所有依赖项都是从提供商注入到工厂的。 这是现实生活中依赖注入 (DI) 的示例。

现在用技术术语来说,依赖注入是一种技术,一个对象(或静态方法)通过该技术提供另一个对象的依赖关系。 所以,把创建对象的任务交给别人,直接使用依赖就叫做依赖注入。

现在将通过技术解释帮助您学习 DI。 将显示何时使用 DI 以及何时使用你不应该

全合一汽车厂

简单汽车工厂

Before going to the technical description first visualize it with a real-life example because you will find a lot of technical stuff to learn dependency injection but the majority of the people can't get the core concept of it.

In the first picture, assume that you have a car factory with a lot of units. A car is actually built in the assembly unit but it needs engine, seats as well as wheels. So an assembly unit is dependent on these all units and they are the dependencies of the factory.

You can feel that now it is too complicated to maintain all of the tasks in this factory because along with the main task (assembling a car in the Assembly unit) you have to also focus on other units. It is now very costly to maintain and the factory building is huge so it takes your extra bucks for rent.

Now, look at the second picture. If you find some provider companies that will provide you with the wheel, seat, and engine for cheaper than your self-production cost then now you don't need to make them in your factory. You can rent a smaller building now just for your assembly unit which will lessen your maintenance task and reduce your extra rental cost. Now you can also focus only on your main task (Car assembly).

Now we can say that all the dependencies for assembling a car are injected on the factory from the providers. It is an example of a real-life Dependency Injection (DI).

Now in the technical word, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. So, transferring the task of creating the object to someone else and directly using the dependency is called dependency injection.

This will help you now to learn DI with a technical explanation. This will show when to use DI and when you should not.

All in one car factory.

Simple car factory

南风起 2024-07-12 21:56:12

是关于依赖注入依赖注入最简单的解释容器我见过:

没有依赖注入

  • 应用程序需要Foo(例如控制器),所以:
  • 应用程序创建Foo
  • 应用程序调用Foo
    • Foo 需要 Bar(例如服务),因此:
    • Foo 创建 Bar
    • Foo 调用 Bar
      • Bar 需要 Bim(服务、存储库、
        ...),所以:
      • Bar 创建 Bim
      • 酒吧做了一些事情

使用依赖注入执行

  • 某些操作 应用程序需要 Foo,而 Foo 又需要 Bar,而 Bar 又需要 Bim,所以:
  • 应用程序创建 Bim
  • 应用程序创建 Bar 并为其提供 Bim
  • 应用程序创建 Foo 并为其提供 Bar
  • 应用程序调用 Foo
    • Foo 调用 Bar
      • 酒吧做了一些事情

使用依赖注入容器执行

  • 某些操作应用程序需要 Foo,因此:
  • 应用程序从容器获取 Foo,因此:
    • 容器创建 Bim
    • 容器创建 Bar 并赋予它 Bim
    • 容器创建 Foo 并赋予它 Bar
  • 应用程序调用 Foo
    • Foo 调用 Bar
      • 酒吧做了一些事情

依赖注入依赖注入容器是不同的事情:

  • 依赖注入是一种编写更好代码的方法
  • DI容器是帮助注入依赖项的工具

您不需要容器来进行依赖项注入。 然而,容器可以帮助你。

This is the most simple explanation about Dependency Injection and Dependency Injection Container I have ever seen:

Without Dependency Injection

  • Application needs Foo (e.g. a controller), so:
  • Application creates Foo
  • Application calls Foo
    • Foo needs Bar (e.g. a service), so:
    • Foo creates Bar
    • Foo calls Bar
      • Bar needs Bim (a service, a repository,
        …), so:
      • Bar creates Bim
      • Bar does something

With Dependency Injection

  • Application needs Foo, which needs Bar, which needs Bim, so:
  • Application creates Bim
  • Application creates Bar and gives it Bim
  • Application creates Foo and gives it Bar
  • Application calls Foo
    • Foo calls Bar
      • Bar does something

Using a Dependency Injection Container

  • Application needs Foo so:
  • Application gets Foo from the Container, so:
    • Container creates Bim
    • Container creates Bar and gives it Bim
    • Container creates Foo and gives it Bar
  • Application calls Foo
    • Foo calls Bar
      • Bar does something

Dependency Injection and dependency Injection Containers are different things:

  • Dependency Injection is a method for writing better code
  • a DI Container is a tool to help injecting dependencies

You don't need a container to do dependency injection. However a container can help you.

谈情不如逗狗 2024-07-12 21:56:12

让我们想象一下,您想去钓鱼:

  • 如果没有依赖注入,您需要自己处理所有事情。 你需要找一条船,买一根鱼竿,寻找鱼饵等等。当然这是可能的,但这会让你承担很多责任。 用软件术语来说,这意味着您必须对所有这些事情执行查找。

  • 通过依赖注入,其他人会负责所有准备工作并为您提供所需的设备。 您将收到(“被注入”)船、钓鱼竿和鱼饵 - 一切都可以使用。

Let's imagine that you want to go fishing:

  • Without dependency injection, you need to take care of everything yourself. You need to find a boat, to buy a fishing rod, to look for bait, etc. It's possible, of course, but it puts a lot of responsibility on you. In software terms, it means that you have to perform a lookup for all these things.

  • With dependency injection, someone else takes care of all the preparation and makes the required equipment available to you. You will receive ("be injected") the boat, the fishing rod and the bait - all ready to use.

内心激荡 2024-07-12 21:56:12

“依赖注入”不就是意味着使用参数化构造函数和公共设置器吗?

James Shore 的文章展示了以下示例进行比较

没有依赖注入的构造函数:

公共类示例 {  
    私有数据库Thingie myDatabase;  

    公共示例(){  
      myDatabase = new DatabaseThingie();  
    }  

    公共无效doStuff(){  
      ...  
      myDatabase.getData();  
      ...  
    }  
  }  
  

具有依赖注入的构造函数:

公共类示例 {  
    私有数据库Thingie myDatabase;  

    公共示例(DatabaseThingie useThisDatabaseInstead){  
      myDatabase = 使用此数据库代替;  
    } 

    公共无效doStuff(){  
      ...  
      myDatabase.getData();  
      ...  
    }  
  } 
  

Doesn't "dependency injection" just mean using parameterized constructors and public setters?

James Shore's article shows the following examples for comparison.

Constructor without dependency injection:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Constructor with dependency injection:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}
二智少女 2024-07-12 21:56:12

使依赖注入概念变得简单易懂。 让我们以开关按钮来切换(开/关)灯泡为例。

如果没有依赖注入,

开关需要事先知道我连接到哪个灯泡(硬编码依赖)。 所以,

切换-> PermanentBulb //开关直接连接到永久灯泡,无法轻松测试

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

使用依赖注入

开关只知道我需要打开/关闭传递给我的灯泡。 所以,

切换-> Bulb1 OR Bulb2 OR NightBulb(注入依赖项)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

修改开关和灯泡的 James 示例:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`

To make Dependency Injection concept simple to understand. Let's take an example of switch button to toggle(on/off) a bulb.

Without Dependency Injection

Switch needs to know beforehand which bulb I am connected to (hard-coded dependency). So,

Switch -> PermanentBulb //switch is directly connected to permanent bulb, testing not possible easily

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

With Dependency Injection

Switch only knows I need to turn on/off whichever Bulb is passed to me. So,

Switch -> Bulb1 OR Bulb2 OR NightBulb (injected dependency)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Modifying James Example for Switch and Bulb:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`
灵芸 2024-07-12 21:56:12

什么是依赖注入 (DI)?

正如其他人所说,依赖注入 (DI) 消除了直接创建其他对象实例以及管理其生命周期的责任我们感兴趣的类(消费者类)是依赖的(在 UML 意义上)。 这些实例通常作为构造函数参数或通过属性设置器传递给我们的消费者类(依赖对象实例化和传递给消费者类的管理通常由控制反转 (IoC) 执行)容器,但那是另一个话题)。

DI、DIP 和 SOLID

具体来说,在 Robert C Martin 的 面向对象设计的SOLID原则DI依赖倒置原则(DIP)DIP 是 SOLID 口头禅的 D - 其他 DIP实现包括服务定位器和插件模式。

DIP 的目标是解耦类之间紧密、具体的依赖关系,而是通过抽象来放松耦合,这可以通过接口抽象类来实现code> 或 纯虚拟类,具体取决于所使用的语言和方法。

如果没有 DIP,我们的代码(我称之为“消费类”)将直接耦合到具体的依赖项,并且通常还承担着了解如何获取和管理此依赖项的实例的责任,即概念上:

"I need to create/use a Foo and invoke method `GetBar()`"

而应用 DIP 后,要求放松了,并且消除了获取和管理 Foo 依赖项的生命周期的问题:

"I need to invoke something which offers `GetBar()`"

为什么使用 DIP(和 DI)?

以这种方式解耦类之间的依赖关系允许用其他也满足抽象先决条件的实现轻松替换这些依赖类(例如,可以使用同一接口的另一个实现来切换依赖关系)。 此外,正如其他人所提到的,通过 DIP 解耦类的最常见原因可能是允许单独测试消费类,因为现在可以对这些相同的依赖项进行存根和/或模拟。

DI 的后果之一是依赖对象实例的生命周期管理不再由消费类控制,因为依赖对象现在被传递到消费类(通过构造函数或 setter 注入)。

这可以用不同的方式来看待:

  • 如果需要保留消费类对依赖项的生命周期控制,则可以通过将用于创建依赖项类实例的(抽象)工厂注入到消费者类中来重新建立控制。 消费者将能够根据需要通过工厂上的Create获取实例,并在完成后处理这些实例。
  • 或者,可以将依赖项实例的生命周期控制交给 IoC 容器(下文将详细介绍)。

何时使用 DI?

  • 何时可能需要用依赖项替换等效实现,
  • 何时需要对类的方法进行单元测试以隔离其依赖项,
  • 何时存在不确定性依赖项的生命周期可能需要实验(例如,嘿,MyDepClass 是线程安全的 - 如果我们将其设为单例并向所有使用者注入相同的实例会怎样?)

示例

这是一个简单的 C# 实现。 给出下面的 Consuming 类:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

虽然看似无害,但它对另外两个类(System.DateTimeSystem.Console)有两个 static 依赖项,这两个类不仅限制了日志输出选项(如果没有人在看,日志记录到控制台将毫无价值),而且更糟糕的是,由于对非确定性系统时钟的依赖,很难自动测试。

然而,我们可以将DIP应用于此类,通过将时间戳问题抽象为依赖关系,并将MyLogger仅耦合到一个简单的接口:

public interface IClock
{
    DateTime Now { get; }
}

我们还可以放松依赖关系在 Console 上到抽象,例如 TextWriter。 依赖注入通常实现为构造函数注入(将抽象传递给依赖项作为参数传递给消费类的构造函数)或 Setter 注入(通过setXyz() setter 或定义了 {set;} 的 .Net 属性)。 构造函数注入是首选,因为这可以保证类在构造后处于正确的状态,并允许将内部依赖字段标记为 readonly (C#) 或 final (爪哇)。 因此,在上面的示例中使用构造函数注入,这给我们留下了:(

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

需要提供具体的Clock,它当然可以恢复为DateTime.Now,并且两个依赖项需要由 IoC 容器通过构造函数注入提供)

可以构建自动单元测试,这明确地证明我们的记录器工作正常,因为我们现在可以控制依赖项 - 时间,并且我们可以监视编写的内容输出:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

后续步骤

依赖注入总是与控制反转容器 (IoC) 相关联,注入(提供)具体的依赖实例,并管理实例的生命周期。 在配置/引导过程中,IoC 容器允许定义以下内容:

  • 每个抽象和配置的具体实现之间的映射(例如“任何时候消费者请求 IBar 时”) code>,返回一个ConcreteBar实例”)
  • 可以为每个依赖项的生命周期管理设置策略,例如为每个消费者实例创建一个新对象,共享一个单例依赖项实例在所有消费者之间,仅在同一线程之间共享相同的依赖项实例,等等。
  • 在 .Net 中,IoC 容器知道诸如 IDisposable 之类的协议,并将承担处理的责任/code> 依赖关系符合配置的生命周期管理。

通常,一旦配置/引导 IoC 容器,它们就会在后台无缝运行,从而使编码人员能够专注于手头的代码,而不必担心依赖关系。

DI友好代码的关键是避免类的静态耦合,并且不要使用new()来创建依赖项

如上面的示例,依赖项的解耦确实需要一些设计工作,对于开发人员来说,有一个范例这种转变需要打破直接新建依赖项的习惯,而是信任容器来管理依赖项。

但好处有很多,特别是能够彻底测试您感兴趣的类别。

注意:POCO / POJO /序列化DTO /实体图/匿名JSON投影等的创建/映射/投影(通过new ..()) - 即“数据”仅“类或记录 - 从方法中使用或返回被视为依赖项(在 UML 意义上)并且不受 DI 约束。 使用 new 来投影这些就可以了。

What is Dependency Injection (DI)?

As others have said, Dependency Injection(DI) removes the responsibility of direct creation, and management of the lifespan, of other object instances upon which our class of interest (consumer class) is dependent (in the UML sense). These instances are instead passed to our consumer class, typically as constructor parameters or via property setters (the management of the dependency object instancing and passing to the consumer class is usually performed by an Inversion of Control (IoC) container, but that's another topic).

DI, DIP and SOLID

Specifically, in the paradigm of Robert C Martin's SOLID principles of Object Oriented Design, DI is one of the possible implementations of the Dependency Inversion Principle (DIP). The DIP is the D of the SOLID mantra - other DIP implementations include the Service Locator, and Plugin patterns.

The objective of the DIP is to decouple tight, concrete dependencies between classes, and instead, to loosen the coupling by means of an abstraction, which can be achieved via an interface, abstract class or pure virtual class, depending on the language and approach used.

Without the DIP, our code (I've called this 'consuming class') is directly coupled to a concrete dependency and is also often burdened with the responsibility of knowing how to obtain, and manage, an instance of this dependency, i.e. conceptually:

"I need to create/use a Foo and invoke method `GetBar()`"

Whereas after application of the DIP, the requirement is loosened, and the concern of obtaining and managing the lifespan of the Foo dependency has been removed:

"I need to invoke something which offers `GetBar()`"

Why use DIP (and DI)?

Decoupling dependencies between classes in this way allows for easy substitution of these dependency classes with other implementations which also fulfil the prerequisites of the abstraction (e.g. the dependency can be switched with another implementation of the same interface). Moreover, as others have mentioned, possibly the most common reason to decouple classes via the DIP is to allow a consuming class to be tested in isolation, as these same dependencies can now be stubbed and/or mocked.

One consequence of DI is that the lifespan management of dependency object instances is no longer controlled by a consuming class, as the dependency object is now passed into the consuming class (via constructor or setter injection).

This can be viewed in different ways:

  • If lifespan control of dependencies by the consuming class needs to be retained, control can be re-established by injecting an (abstract) factory for creating the dependency class instances, into the consumer class. The consumer will be able to obtain instances via a Create on the factory as needed, and dispose of these instances once complete.
  • Or, lifespan control of dependency instances can be relinquished to an IoC container (more about this below).

When to use DI?

  • Where there likely will be a need to substitute a dependency for an equivalent implementation,
  • Any time where you will need to unit test the methods of a class in isolation of its dependencies,
  • Where uncertainty of the lifespan of a dependency may warrant experimentation (e.g. Hey, MyDepClass is thread safe - what if we make it a singleton and inject the same instance into all consumers?)

Example

Here's a simple C# implementation. Given the below Consuming class:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Although seemingly innocuous, it has two static dependencies on two other classes, System.DateTime and System.Console, which not only limit the logging output options (logging to console will be worthless if no one is watching), but worse, it is difficult to automatically test given the dependency on a non-deterministic system clock.

We can however apply DIP to this class, by abstracting out the the concern of timestamping as a dependency, and coupling MyLogger only to a simple interface:

public interface IClock
{
    DateTime Now { get; }
}

We can also loosen the dependency on Console to an abstraction, such as a TextWriter. Dependency Injection is typically implemented as either constructor injection (passing an abstraction to a dependency as a parameter to the constructor of a consuming class) or Setter Injection (passing the dependency via a setXyz() setter or a .Net Property with {set;} defined). Constructor Injection is preferred, as this guarantees the class will be in a correct state after construction, and allows the internal dependency fields to be marked as readonly (C#) or final (Java). So using constructor injection on the above example, this leaves us with:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(A concrete Clock needs to be provided, which of course could revert to DateTime.Now, and the two dependencies need to be provided by an IoC container via constructor injection)

An automated Unit Test can be built, which definitively proves that our logger is working correctly, as we now have control over the dependencies - the time, and we can spy on the written output:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Next Steps

Dependency injection is invariably associated with an Inversion of Control container(IoC), to inject (provide) the concrete dependency instances, and to manage lifespan instances. During the configuration / bootstrapping process, IoC containers allow the following to be defined:

  • mapping between each abstraction and the configured concrete implementation (e.g. "any time a consumer requests an IBar, return a ConcreteBar instance")
  • policies can be set up for the lifespan management of each dependency, e.g. to create a new object for each consumer instance, to share a singleton dependency instance across all consumers, to share the same dependency instance only across the same thread, etc.
  • In .Net, IoC containers are aware of protocols such as IDisposable and will take on the responsibility of Disposing dependencies in line with the configured lifespan management.

Typically, once IoC containers have been configured / bootstrapped, they operate seamlessly in the background allowing the coder to focus on the code at hand rather than worrying about dependencies.

The key to DI-friendly code is to avoid static coupling of classes, and not to use new() for the creation of Dependencies

As per above example, decoupling of dependencies does require some design effort, and for the developer, there is a paradigm shift needed to break the habit of newing dependencies directly, and instead trusting the container to manage dependencies.

But the benefits are many, especially in the ability to thoroughly test your class of interest.

Note : The creation / mapping / projection (via new ..()) of POCO / POJO / Serialization DTOs / Entity Graphs / Anonymous JSON projections et al - i.e. "Data only" classes or records - used or returned from methods are not regarded as Dependencies (in the UML sense) and not subject to DI. Using new to project these is just fine.

请你别敷衍 2024-07-12 21:56:12

依赖注入 (DI) 的全部目的是保持应用程序源代码干净稳定

  • 依赖初始化代码干净
  • 稳定< /strong> 无论使用什么依赖项

实际上,每个设计模式都会分离关注点以使未来的更改影响最小文件。

DI 的具体领域是依赖配置和初始化的委托。

示例:使用 shell 脚本进行 DI

如果您偶尔在 Java 之外工作,请回想一下 source 在许多脚本语言(Shell、Tcl 等)中的常用方式,甚至是 import Python 被滥用于此目的)。

考虑简单的 dependent.sh 脚本:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

该脚本是相关的:它自己不会成功执行(archive_files 未定义)。

您在 archive_files_zip.sh 实现脚本中定义 archive_files(在本例中使用 zip):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

而不是 source-ing直接在依赖项中执行脚本,您使用一个包含两个“组件”的 injector.sh“容器”:

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

archive_files 依赖项刚刚已注入依赖脚本中。

您可以使用 tarxz 注入实现 archive_files 的依赖项。

示例:删除 DI

如果 dependent.sh 脚本直接使用依赖项,则该方法将称为依赖项查找(与依赖项注入相反):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

现在的问题是依赖的“组件”必须自己执行初始化。

“组件”的源代码既不是干净也不是稳定,因为依赖项初始化的每次更改都需要“组件”源代码文件的新版本。

最后的话

DI 并没有像 Java 框架那样受到重视和普及。

但这是一种通用方法来划分以下方面的关注点:

  • 应用程序开发单个源代码发布生命周期)
  • 应用程序部署多个 > 具有独立生命周期的目标环境)

仅将配置与依赖项查找结合使用并无帮助,因为每个依赖项的配置参数数量(例如新的身份验证类型)以及受支持的依赖项类型的数量(例如新的身份验证类型)可能会发生变化。数据库类型)。

The whole point of Dependency Injection (DI) is to keep application source code clean and stable:

  • clean of dependency initialization code
  • stable regardless of dependency used

Practically, every design pattern separates concerns to make future changes affect minimum files.

The specific domain of DI is delegation of dependency configuration and initialization.

Example: DI with shell script

If you occasionally work outside of Java, recall how source is often used in many scripting languages (Shell, Tcl, etc., or even import in Python misused for this purpose).

Consider simple dependent.sh script:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

The script is dependent: it won't execute successfully on its own (archive_files is not defined).

You define archive_files in archive_files_zip.sh implementation script (using zip in this case):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Instead of source-ing implementation script directly in the dependent one, you use an injector.sh "container" which wraps both "components":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

The archive_files dependency has just been injected into dependent script.

You could have injected dependency which implements archive_files using tar or xz.

Example: removing DI

If dependent.sh script used dependencies directly, the approach would be called dependency lookup (which is opposite to dependency injection):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Now the problem is that dependent "component" has to perform initialization itself.

The "component"'s source code is neither clean nor stable because every changes in initialization of dependencies requires new release for "components"'s source code file as well.

Last words

DI is not as largely emphasized and popularized as in Java frameworks.

But it's a generic approach to split concerns of:

  • application development (single source code release lifecycle)
  • application deployment (multiple target environments with independent lifecycles)

Using configuration only with dependency lookup does not help as number of configuration parameters may change per dependency (e.g. new authentication type) as well as number of supported types of dependencies (e.g. new database type).

葬﹪忆之殇 2024-07-12 21:56:12

以上答案都很好,我的目的是用简单的方式解释这个概念,以便任何没有编程知识的人也能理解概念

依赖注入是帮助我们创建复杂的设计模式之一系统以更简单的方式。

我们可以在日常生活中看到这种模式的广泛应用。
其中一些示例包括磁带录音机、VCD、CD 驱动器等。

Reel-to-reel 便携式磁带录音机,20 世纪中期。

上图是 Reel-to-reel 便携式磁带录音机,20 世纪中期的图像世纪。 来源

录音机的主要用途是录制或播放声音。

在设计系统时,需要一个卷轴来录制或播放声音或音乐。 设计该系统有两种可能性:

  1. 我们可以将卷轴放置在机器内部,
  2. 我们可以为卷轴提供一个可以放置它的钩子。

如果我们使用第一个,我们需要打开机器来更换卷轴。
如果我们选择第二种,即为卷轴放置一个钩子,我们将获得通过更换卷轴播放任何音乐的额外好处。 并且还减少了仅播放卷轴中的任何内容的功能。

同样,依赖注入是将依赖关系外部化以仅关注组件的特定功能的过程,以便独立组件可以耦合在一起形成复杂的系统。

我们通过使用依赖注入实现的主要好处。

  • 高内聚、松耦合。
  • 将依赖外部化,只关注责任。
  • 将事物作为组件并组合起来形成具有高性能的大型系统。
  • 它有助于开发高质量的组件,因为它们是独立开发的并且经过适当的测试。
  • 如果一个组件出现故障,它有助于用另一个组件替换该组件。

如今,这些概念构成了编程世界中众所周知的框架的基础。
Spring Angular 等是建立在这个概念之上的众所周知的软件框架

依赖注入是一种模式,用于创建其他对象所依赖的对象实例,而无需在编译时知道哪个类将用于提供该功能或简单地向对象注入属性的方式称为依赖注入。

依赖注入示例

之前我们正在编写这样的代码

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

通过依赖注入,依赖注入器将为我们取消实例化

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 
   
  dependentObject.someMethod();
}

您还可以阅读

控制反转与控制反转之间的区别 依赖注入

All the above answers are good, my aim is to explain the concept in a simple way so that anyone without a programming knowledge can also understand concept

Dependency injection is one of the design pattern that help us to create complex systems in a simpler manner.

We can see a wide variety of application of this pattern in our day to day life.
Some of the examples are Tape recorder, VCD, CD Drive etc.

Reel-to-reel portable tape recorder, mid-20th century.

The above image is an image of Reel-to-reel portable tape recorder, mid-20th century. Source.

The primary intention of a tape recorder machine is to record or playback sound.

While designing a system it require a reel to record or playback sound or music. There are two possibilities for designing this system

  1. we can place the reel inside the machine
  2. we can provide a hook for the reel where it can be placed.

If we use the first one we need to open the machine to change the reel.
if we opt for the second one, that is placing a hook for reel, we are getting an added benefit of playing any music by changing the reel. and also reducing the function only to playing whatever in the reel.

Like wise dependency injection is the process of externalizing the dependencies to focus only on the specific functionality of the component so that independent components can be coupled together to form a complex system.

The main benefits we achieved by using dependency injection.

  • High cohesion and loose coupling.
  • Externalizing dependency and looking only on responsibility.
  • Making things as components and to combine to form a large systems with high capabilities.
  • It helps to develop high quality components since they are independently developed they are properly tested.
  • It helps to replace the component with another if one fails.

Now a days these concept forms the basis of well known frameworks in programming world.
The Spring Angular etc are the well-known software frameworks built on the top of this concept

Dependency injection is a pattern used to create instances of objects that other objects rely upon without knowing at compile time which class will be used to provide that functionality or simply the way of injecting properties to an object is called dependency injection.

Example for Dependency injection

Previously we are writing code like this

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

With Dependency injection, the dependency injector will take off the instantiation for us

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 
   
  dependentObject.someMethod();
}

You can also read

Difference between Inversion of Control & Dependency Injection

面犯桃花 2024-07-12 21:56:12

例如,我们有 2 个类 ClientServiceClient 将使用 Service

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

无需依赖注入

方式 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

方式 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

方式 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) 使用

Client client = new Client();
client.doSomeThingInService();

优点

  • 简单

缺点

  • 难以测试 Client
  • 当我们更改 时Service 构造函数,我们需要在创建 Service 对象的所有地方更改代码

使用依赖注入

方式 1) 构造函数注入

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

使用

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

方式2) Setter注入

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

使用

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

方式3)接口注入

检查https://en.wikipedia.org/wiki/Dependency_injection

===

现在,这段代码已经遵循依赖注入并且更容易测试Client 类。
然而,我们仍然多次使用new Service(),并且当改变Service构造函数时就不好了。 为了防止这种情况,我们可以使用 DI 注入器,例如
1) 简单手册 Injector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

使用

Service service = Injector.provideService();

2) 使用库:对于 Android dagger2

优点

  • 让测试更容易
  • 当你改变Service时,你只需要在Injector类中改变它
  • 如果你使用使用构造函数注入 >,当你查看Client的构造函数时,你会看到Client类有多少依赖

缺点

  • 如果你使用构造函数注入Service对象是在Client创建时创建的,有时我们使用Client类中的函数而不使用Service code> 这样创建的 Service 是浪费

依赖注入定义

https://en.wikipedia。 org/wiki/Dependency_injection

依赖项是一个可以使用的对象(Service
注入是将依赖项 (Service) 传递给使用它的依赖对象 (Client)

Example, we have 2 class Client and Service. Client will use Service

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Without Dependency Injection

Way 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Way 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Way 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Using

Client client = new Client();
client.doSomeThingInService();

Advantages

  • Simple

Disadvantages

  • Hard for test Client class
  • When we change Service constructor, we need to change code in all place create Service object

Use Dependency Injection

Way 1) Constructor injection

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Using

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Way 2) Setter injection

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Using

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Way 3) Interface injection

Check https://en.wikipedia.org/wiki/Dependency_injection

===

Now, this code is already follow Dependency Injection and it is easier for test Client class.
However, we still use new Service() many time and it is not good when change Service constructor. To prevent it, we can use DI injector like
1) Simple manual Injector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

Using

Service service = Injector.provideService();

2) Use library: For Android dagger2

Advantages

  • Make test easier
  • When you change the Service, you only need to change it in Injector class
  • If you use use Constructor Injection, when you look at constructor of Client, you will see how many dependency of Client class

Disadvantages

  • If you use use Constructor Injection, the Service object is created when Client created, sometime we use function in Client class without use Service so created Service is wasted

Dependency Injection definition

https://en.wikipedia.org/wiki/Dependency_injection

A dependency is an object that can be used (Service)
An injection is the passing of a dependency (Service) to a dependent object (Client) that would use it

∞觅青森が 2024-07-12 21:56:12

什么是依赖注入?

依赖注入(DI)是指将相互依赖的对象解耦。 假设对象 A 依赖于对象 B,因此我们的想法是将这些对象彼此解耦。 我们不需要使用 new 关键字对对象进行硬编码,而是在运行时共享对象的依赖关系,而不管编译时间。
如果我们谈论

依赖注入在 Spring 中的工作原理:

我们不需要使用 new 关键字对对象进行硬编码,而是在配置文件中定义 bean 依赖项。 spring 容器将负责连接所有内容。

控制反转 (IOC)

IOC 是一个通用概念,可以用多种不同的方式表达,依赖注入就是 IOC 的一个具体示例。

依赖注入的两种类型:

  1. 构造函数注入
  2. Setter 注入

1. 基于构造函数的依赖注入:

当容器调用带有多个参数的类构造函数时,基于构造函数的 DI 就完成了,每个参数代表对其他类的依赖。

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. 基于 Setter 的依赖注入:

基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

笔记:
使用构造函数参数来实现强制依赖项,使用 setter 来实现可选依赖项是一个很好的经验法则。 请注意,如果我们在 setter 上使用基于 @Required 注释的注释,则可以使用 setter 作为必需的依赖项。

What is dependency Injection?

Dependency Injection(DI) means to decouple the objects which are dependent on each other. Say object A is dependent on Object B so the idea is to decouple these object from each other. We don’t need to hard code the object using new keyword rather sharing dependencies to objects at runtime in spite of compile time.
If we talk about

How Dependency Injection works in Spring:

We don’t need to hard code the object using new keyword rather define the bean dependency in the configuration file. The spring container will be responsible for hooking up all.

Inversion of Control (IOC)

IOC is a general concept and it can be expressed in many different ways and Dependency Injection is one concrete example of IOC.

Two types of Dependency Injection:

  1. Constructor Injection
  2. Setter Injection

1. Constructor-based dependency injection:

Constructor-based DI is accomplished when the container invokes a class constructor with a number of arguments, each representing a dependency on other class.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Setter-based dependency injection:

Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

NOTE:
It is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies. Note that the if we use annotation based than @Required annotation on a setter can be used to make setters as a required dependencies.

ら栖息 2024-07-12 21:56:12

这意味着对象应该只具有完成其工作所需的依赖项,并且依赖项应该很少。 此外,如果可能的话,对象的依赖关系应该依赖于接口,而不是“具体”对象。 (具体对象是使用关键字 new 创建的任何对象。)松散耦合促进了更大的可重用性、更容易的可维护性,并允许您轻松地提供“模拟”对象来代替昂贵的服务。

“依赖注入”(DI)也称为“控制反转”(IoC),可以用作促进这种松散耦合的技术。

实现 DI 有两种主要方法:

  1. 构造函数注入
  2. Setter 注入

构造函数注入

这是将对象依赖项传递给其构造函数的技术。

请注意,构造函数接受接口而不是具体对象。 另请注意,如果 orderDao 参数为 null,则会引发异常。 这强调了接收有效依赖的重要性。 在我看来,构造函数注入是为对象提供依赖项的首选机制。 开发人员在调用对象时很清楚需要将哪些依赖项提供给“Person”对象才能正确执行。

Setter 注入

但请考虑以下示例...假设您有一个类,其中有 10 个没有依赖项的方法,但您要添加一个确实依赖于 IDAO 的新方法。 您可以更改构造函数以使用构造函数注入,但这可能会迫使您更改整个地方的所有构造函数调用。 或者,您可以只添加一个接受依赖项的新构造函数,但是开发人员如何轻松知道何时使用一个构造函数而不是另一个构造函数。 最后,如果创建依赖项的成本非常昂贵,那么为什么要创建它并将其传递给构造函数,而它可能很少使用呢? “Setter 注入”是另一种可以在这种情况下使用的 DI 技术。

Setter 注入不会强制将依赖项传递给构造函数。 相反,依赖关系被设置到需要的对象公开的公共属性上。 正如前面所暗示的,这样做的主要动机包括:

  1. 支持依赖项注入,而无需修改遗留类的构造函数。
  2. 允许尽可能晚地且仅在需要时创建昂贵的资源或服务。

以下是上述代码的示例:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

It means that objects should only have as many dependencies as is needed to do their job and the dependencies should be few. Furthermore, an object’s dependencies should be on interfaces and not on “concrete” objects, when possible. (A concrete object is any object created with the keyword new.) Loose coupling promotes greater reusability, easier maintainability, and allows you to easily provide “mock” objects in place of expensive services.

The “Dependency Injection” (DI) is also known as “Inversion of Control” (IoC), can be used as a technique for encouraging this loose coupling.

There are two primary approaches to implementing DI:

  1. Constructor injection
  2. Setter injection

Constructor injection

It’s the technique of passing objects dependencies to its constructor.

Note that the constructor accepts an interface and not concrete object. Also, note that an exception is thrown if the orderDao parameter is null. This emphasizes the importance of receiving a valid dependency. Constructor Injection is, in my opinion, the preferred mechanism for giving an object its dependencies. It is clear to the developer while invoking the object which dependencies need to be given to the “Person” object for proper execution.

Setter Injection

But consider the following example… Suppose you have a class with ten methods that have no dependencies, but you’re adding a new method that does have a dependency on IDAO. You could change the constructor to use Constructor Injection, but this may force you to changes to all constructor calls all over the place. Alternatively, you could just add a new constructor that takes the dependency, but then how does a developer easily know when to use one constructor over the other. Finally, if the dependency is very expensive to create, why should it be created and passed to the constructor when it may only be used rarely? “Setter Injection” is another DI technique that can be used in situations such as this.

Setter Injection does not force dependencies to be passed to the constructor. Instead, the dependencies are set onto public properties exposed by the object in need. As implied previously, the primary motivators for doing this include:

  1. Supporting dependency injection without having to modify the constructor of a legacy class.
  2. Allowing expensive resources or services to be created as late as possible and only when needed.

Here is the example of how the above code would look like:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;
波浪屿的海角声 2024-07-12 21:56:12

我能想到的最好的比喻是手术室里的外科医生和他的助手,外科医生是主要人物,他的助手在他需要时提供各种手术部件,以便外科医生可以专注于其中一个他最擅长的事情(手术)。 如果没有助手,外科医生每次需要部件时都必须自己去获取部件。

DI 简称 DI,是一种通过向组件提供依赖组件来消除组件获取依赖组件的常见附加责任(负担)的技术。

DI 让您更接近单一职责 (SR) 原则,就像外科医生可以专注于手术

何时使用 DI :我建议在几乎所有生产项目(小/大)中使用 DI,特别是在不断变化的业务环境中:)

原因:因为您希望您的代码易于测试、可模拟等,以便您可以快速测试您的代码改变并推向市场。 此外,当你有很多很棒的免费工具/框架来支持你的代码库之旅时,你为什么不呢?

The best analogy I can think of is the surgeon and his assistant(s) in an operation theater, where the surgeon is the main person and his assistant who provides the various surgical components when he needs it so that the surgeon can concentrate on the one thing he does best (surgery). Without the assistant the surgeon has to get the components himself every time he needs one.

DI for short, is a technique to remove a common additional responsibility (burden) on components to fetch the dependent components, by providing them to it.

DI brings you closer to the Single Responsibility (SR) principle, like the surgeon who can concentrate on surgery.

When to use DI : I would recommend using DI in almost all production projects ( small/big), particularly in ever changing business environments :)

Why : Because you want your code to be easily testable, mockable etc so that you can quickly test your changes and push it to the market. Besides why would you not when you there are lots of awesome free tools/frameworks to support you in your journey to a codebase where you have more control.

童话 2024-07-12 21:56:12

依赖注入意味着代码的一部分(例如类)以模块化方式访问依赖项(代码的其他部分,例如它所依赖的其他类),而无需对它们进行硬编码(因此它们可以自由更改或覆盖,甚至根据需要在其他时间加载)

(还有 PS,是的,它已经成为一个过度炒作的 25 美元名称,一个相当简单的概念),我的 .25 美分

Dependency Injection means a way (actually any-way) for one part of code (e.g a class) to have access to dependencies (other parts of code, e.g other classes, it depends upon) in a modular way without them being hardcoded (so they can change or be overriden freely, or even be loaded at another time, as needed)

(and ps , yes it has become an overly-hyped 25$ name for a rather simple, concept), my .25 cents

对你再特殊 2024-07-12 21:56:12

流行的答案是没有帮助的,因为它们以一种没有用的方式定义依赖注入。 我们一致认为,“依赖”是指我们的对象 X 需要的一些预先存在的其他对象。 ”时,我们并不是说我们正在执行“依赖注入”

$foo = Foo->new($bar);

但是,当我们说“我们只是调用将参数传递到构造函数中 。 自从构造函数被发明以来,我们就一直定期这样做。

“依赖注入”被认为是一种“控制反转”,这意味着从调用者那里取出了一些逻辑。 当调用者传入参数时情况并非如此,因此如果是 DI,DI 并不意味着控制反转。

DI 意味着调用者和构造函数之间有一个管理依赖关系的中间级别。 Makefile 是依赖注入的一个简单示例。 “调用者”是在命令行中输入“make bar”的人,“构造器”是编译器。 Makefile 指定 bar 依赖于 foo,并且它

gcc -c foo.cpp; gcc -c bar.cpp

在执行 a 之前先执行 a。

gcc foo.o bar.o -o bar

输入“make bar”的人不需要知道 bar 依赖于 foo。 依赖关系被注入到“make bar”和 gcc 之间。

中间级别的主要目的不仅仅是将依赖项传递给构造函数,而是将所有依赖项列出在只是一个地方,并对编码器隐藏它们(不是让编码器提供它们)。

通常,中间层为构造的对象提供工厂,它必须提供每个请求的对象类型必须满足的角色。 这是因为通过隐藏构造细节的中间级别,您已经遭受了工厂施加的抽象惩罚,因此您不妨使用工厂。

The popular answers are unhelpful, because they define dependency injection in a way that isn't useful. Let's agree that by "dependency" we mean some pre-existing other object that our object X needs. But we don't say we're doing "dependency injection" when we say

$foo = Foo->new($bar);

We just call that passing parameters into the constructor. We've been doing that regularly ever since constructors were invented.

"Dependency injection" is considered a type of "inversion of control", which means that some logic is taken out of the caller. That isn't the case when the caller passes in parameters, so if that were DI, DI would not imply inversion of control.

DI means there is an intermediate level between the caller and the constructor which manages dependencies. A Makefile is a simple example of dependency injection. The "caller" is the person typing "make bar" on the command line, and the "constructor" is the compiler. The Makefile specifies that bar depends on foo, and it does a

gcc -c foo.cpp; gcc -c bar.cpp

before doing a

gcc foo.o bar.o -o bar

The person typing "make bar" doesn't need to know that bar depends on foo. The dependency was injected between "make bar" and gcc.

The main purpose of the intermediate level is not just to pass in the dependencies to the constructor, but to list all the dependencies in just one place, and to hide them from the coder (not to make the coder provide them).

Usually the intermediate level provides factories for the constructed objects, which must provide a role that each requested object type must satisfy. That's because by having an intermediate level that hides the details of construction, you've already incurred the abstraction penalty imposed by factories, so you might as well use factories.

倾`听者〃 2024-07-12 21:56:12

摘自本书,“基础良好的 Java 开发人员:Java 7 和多语言编程的重要技术

DI 是 IoC 的一种特殊形式,查找依赖项的过程是
超出当前执行代码的直接控制范围。

From the Book, 'Well-Grounded Java Developer: Vital techniques of Java 7 and polyglot programming

DI is a particular form of IoC, whereby the process of finding your dependencies is
outside the direct control of your currently executing code.

药祭#氼 2024-07-12 21:56:12

依赖注入是解决通常被称为“依赖混淆”需求的一种可能的解决方案。 依赖关系混淆是一种从向需要该依赖关系的类提供依赖关系的过程中去除“明显”性质的方法,从而以某种方式混淆向所述类提供所述依赖关系。 这不一定是坏事。 事实上,通过混淆向类提供依赖项的方式,类外部的某些东西负责创建依赖项,这意味着在各种情况下,可以向类提供依赖项的不同实现,而无需进行任何更改到班级。 这对于在生产和测试模式之间切换(例如,使用“模拟”服务依赖项)非常有用。

不幸的是,不好的部分是,有些人认为您需要一个专门的框架来进行依赖项混淆,并且如果您选择不使用特定的框架来执行此操作,那么您在某种程度上是一个“次要”程序员。 许多人相信的另一个极其令人不安的神话是,依赖项注入是实现依赖项混淆的唯一方法。 从历史上看,这显然是 100% 错误的,但你很难说服一些人,有依赖注入的替代方案来满足你的依赖混淆需求。

程序员多年来一直了解依赖关系混淆的要求,并且在依赖关系注入之前和之后已经发展了许多替代解决方案。 有工厂模式,但也有许多使用 ThreadLocal 的选项,其中不需要注入特定实例 - 依赖项被有效地注入到线程中,其优点是使对象可用(通过方便的静态 getter 方法)任何需要它的类,而无需向需要它的类添加注释并设置复杂的 XML“粘合剂”来​​实现它。 当您的依赖项需要持久性(JPA/JDO 或其他)时,它允许您更轻松地实现“透明持久性”,并且域模型和业务模型类纯粹由 POJO 组成(即没有特定于框架/锁定在注释中)。

Dependency injection is one possible solution to what could generally be termed the "Dependency Obfuscation" requirement. Dependency Obfuscation is a method of taking the 'obvious' nature out of the process of providing a dependency to a class that requires it and therefore obfuscating, in some way, the provision of said dependency to said class. This is not necessarily a bad thing. In fact, by obfuscating the manner by which a dependency is provided to a class then something outside the class is responsible for creating the dependency which means, in various scenarios, a different implementation of the dependency can be supplied to the class without making any changes to the class. This is great for switching between production and testing modes (eg., using a 'mock' service dependency).

Unfortunately the bad part is that some people have assumed you need a specialized framework to do dependency obfuscation and that you are somehow a 'lesser' programmer if you choose not to use a particular framework to do it. Another, extremely disturbing myth, believed by many, is that dependency injection is the only way of achieving dependency obfuscation. This is demonstrably and historically and obviously 100% wrong but you will have trouble convincing some people that there are alternatives to dependency injection for your dependency obfuscation requirements.

Programmers have understood the dependency obfuscation requirement for years and many alternative solutions have evolved both before and after dependency injection was conceived. There are Factory patterns but there are also many options using ThreadLocal where no injection to a particular instance is needed - the dependency is effectively injected into the thread which has the benefit of making the object available (via convenience static getter methods) to any class that requires it without having to add annotations to the classes that require it and set up intricate XML 'glue' to make it happen. When your dependencies are required for persistence (JPA/JDO or whatever) it allows you to achieve 'tranaparent persistence' much easier and with domain model and business model classes made up purely of POJOs (i.e. no framework specific/locked in annotations).

淡写薰衣草的香 2024-07-12 21:56:12

5 岁孩子的依赖注入。

当你自己从冰箱里取出东西时,可能会引起问题。 你可能会把门开着,你可能会得到一些妈妈或爸爸不希望你拥有的东西。 您甚至可能正在寻找我们没有或已经过期的东西。

您应该做的是陈述需求,“我需要在午餐时喝点东西”,然后我们会确保您坐下来吃饭时有东西。

Dependency Injection for 5 year olds.

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might be even looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.

无需解释 2024-07-12 21:56:12

简而言之,依赖注入(DI)是消除不同对象之间的依赖关系或紧密耦合的方法。 依赖注入为每个对象提供了有凝聚力的行为。

DI 是 Spring 的 IOC 原则的实现,即“不要打电话给我们,我们会打电话给你”。 使用依赖注入程序员不需要使用new关键字创建对象。

对象一旦加载到 Spring 容器中,然后我们就可以在需要时通过使用 getBean(String beanName) 方法从 Spring 容器中获取这些对象来重用它们。

In simple words dependency injection (DI) is the way to remove dependencies or tight coupling between different object. Dependency Injection gives a cohesive behavior to each object.

DI is the implementation of IOC principal of Spring which says "Don't call us we will call you". Using dependency injection programmer doesn't need to create object using the new keyword.

Objects are once loaded in Spring container and then we reuse them whenever we need them by fetching those objects from Spring container using getBean(String beanName) method.

紧拥背影 2024-07-12 21:56:12

我想既然每个人都为 DI 编写过,让我问几个问题。

  1. 当你有 DI 的配置时,所有实际实现(而不是接口)将被注入到一个类中(例如控制器的服务)为什么这不是某种硬编码?
  2. 如果我想在运行时更改对象怎么办? 例如,我的配置已经说明当我实例化 MyController 时,将 FileLogger 作为 ILogger 注入。 但我可能想注入 DatabaseLogger。
  3. 每次我想要更改 AClass 需要的对象时,我现在都需要查看两个地方 - 类本身和配置文件。 这如何让生活变得更轻松?
  4. 如果不注入AClass的Aproperty,是不是更难模拟出来?
  5. 回到第一个问题。 如果使用 new object() 不好,为什么我们要注入实现而不是接口呢? 我想你们很多人都说我们实际上是在注入接口,但配置使您指定该接口的实现..不是在运行时..它在编译时是硬编码的。

这是基于 @Adam N 发布的答案。

为什么PersonService 不再需要担心GroupMembershipService 呢? 您刚刚提到 GroupMembership 有多个它依赖的东西(对象/属性)。 如果 PService 中需要 GMService,则您可以将其作为属性。 无论您是否注入它,您都可以模拟它。 我唯一希望注入的情况是 GMService 有更具体的子类,而您直到运行时才知道这些子类。 然后你想要注入子类。 或者,如果您想将其用作单例或原型。 老实说,配置文件对所有内容进行了硬编码,包括在编译时注入类型(接口)的子类。

编辑

Jose Maria Arranz 对 DI 的精彩评论

< em>DI 通过消除确定依赖方向和编写任何粘合代码的需要来增加内聚性。

错。 依赖项的方向是 XML 形式或作为注释,您的依赖项被编写为 XML 代码和注释。 XML 和注释是源代码。

DI 通过使所有组件模块化(即可替换)并具有明确定义的相互接口来减少耦合。

错误。 您不需要 DI 框架来构建基于接口的模块化代码。

关于可替换:使用非常简单的 .properties 存档和 Class.forName 您可以定义哪些类可以更改。 如果您的代码的任何类都可以更改,那么 Java 不适合您,请使用脚本语言。 顺便说一句:如果不重新编译,注释就无法更改。

在我看来,使用 DI 框架的唯一原因只有一个:减少样板。 有了一个做得好的工厂系统,您可以做与您首选的 DI 框架相同、更受控制、更可预测的事情,DI 框架承诺减少代码(XML 和注释也是源代码)。 问题是,在非常非常简单的情况下(每个类一个实例和类似的情况),这种样板文件的减少是真实存在的,有时在现实世界中选择适当的服务对象并不像将类映射到单例对象那么容易。

I think since everyone has written for DI, let me ask a few questions..

  1. When you have a configuration of DI where all the actual implementations(not interfaces) that are going to be injected into a class (for e.g services to a controller) why is that not some sort of hard-coding?
  2. What if I want to change the object at runtime? For example, my config already says when I instantiate MyController, inject for FileLogger as ILogger. But I might want to inject DatabaseLogger.
  3. Every time I want to change what objects my AClass needs, I need to now look into two places - The class itself and the configuration file. How does that make life easier?
  4. If Aproperty of AClass is not injected, is it harder to mock it out?
  5. Going back to the first question. If using new object() is bad, how come we inject the implementation and not the interface? I think a lot of you are saying we're in fact injecting the interface but the configuration makes you specify the implementation of that interface ..not at runtime .. it is hardcoded during compile time.

This is based on the answer @Adam N posted.

Why does PersonService no longer have to worry about GroupMembershipService? You just mentioned GroupMembership has multiple things(objects/properties) it depends on. If GMService was required in PService, you'd have it as a property. You can mock that out regardless of whether you injected it or not. The only time I'd like it to be injected is if GMService had more specific child classes, which you wouldn't know until runtime. Then you'd want to inject the subclass. Or if you wanted to use that as either singleton or prototype. To be honest, the configuration file has everything hardcoded as far as what subclass for a type (interface) it is going to inject during compile time.

EDIT

A nice comment by Jose Maria Arranz on DI

DI increases cohesion by removing any need to determine the direction of dependency and write any glue code.

False. The direction of dependencies is in XML form or as annotations, your dependencies are written as XML code and annotations. XML and annotations ARE source code.

DI reduces coupling by making all of your components modular (i.e. replaceable) and have well-defined interfaces to each other.

False. You do not need a DI framework to build a modular code based on interfaces.

About replaceable: with a very simple .properties archive and Class.forName you can define which classes can change. If ANY class of your code can be changed, Java is not for you, use an scripting language. By the way: annotations cannot be changed without recompiling.

In my opinion there is one only reason for DI frameworks: boiler plate reduction. With a well done factory system you can do the same, more controlled and more predictable as your preferred DI framework, DI frameworks promise code reduction (XML and annotations are source code too). The problem is this boiler plate reduction is just real in very very simple cases (one instance-per class and similar), sometimes in the real world picking the appropriated service object is not as easy as mapping a class to a singleton object.

等数载,海棠开 2024-07-12 21:56:12

依赖注入(DI)是依赖倒置原则(DIP)实践的一部分,也称为控制反转(IoC)。 基本上,您需要进行 DIP,因为您希望使代码更加模块化和单元可测试,而不仅仅是一个整体系统。 因此,您开始识别可以从类中分离并抽象出来的代码部分。 现在需要从类外部注入抽象的实现。 通常这可以通过构造函数来完成。 因此,您创建一个接受抽象作为参数的构造函数,这称为依赖项注入(通过构造函数)。 有关 DIP、DI 和 IoC 容器的更多说明,您可以阅读 这里

Dependency Injection (DI) is part of Dependency Inversion Principle (DIP) practice, which is also called Inversion of Control (IoC). Basically you need to do DIP because you want to make your code more modular and unit testable, instead of just one monolithic system. So you start identifying parts of the code that can be separated from the class and abstracted away. Now the implementation of the abstraction need to be injected from outside of the class. Normally this can be done via constructor. So you create a constructor that accepts the abstraction as a parameter, and this is called dependency injection (via constructor). For more explanation about DIP, DI, and IoC container you can read Here

素手挽清风 2024-07-12 21:56:12

依赖注入(DI)是设计模式中的一种,它使用 OOP 的基本特征——一个对象与另一个对象的关系。 虽然继承继承一个对象来执行更复杂和更具体的另一个对象,但关系或关联只是使用属性从一个对象创建指向另一个对象的指针。 DI 的强大之处在于与 OOP 的其他功能(如接口和隐藏代码)相结合。
假设,我们在图书馆有一个客户(订阅者),为了简单起见,他只能借一本书。

书籍界面:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

接下来我们可以有多种书籍; 其中一种类型是小说:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

现在订阅者可以与这本书关联:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

所有三个类都可以隐藏以用于其自己的实现。 现在我们可以使用这段代码进行 DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

有很多不同的方法可以使用依赖注入。 可以将它与Singleton等结合起来,但基本上它只是通过在另一个对象中创建对象类型的属性来实现关联。
有用性仅在于功能,我们应该一次又一次编写的代码始终为我们准备好并完成。 这就是为什么 DI 与控制反转 (IoC) 如此紧密地结合在一起,这意味着我们的程序将控制权传递给另一个正在运行的模块,该模块将 bean 注入到我们的代码中。 (每个可以注入的对象都可以被签名或视为 Bean。)例如,在 Spring 中,它是通过创建和初始化 ApplicationContext 容器来完成的,它为我们完成了这项工作。 我们只需在代码中创建 Context 并调用 beans 的初始化。 在那一刻注射已经自动完成。

Dependency Injection (DI) is one from Design Patterns, which uses the basic feature of OOP - the relationship in one object with another object. While inheritance inherits one object to do more complex and specific another object, relationship or association simply creates a pointer to another object from one object using attribute. The power of DI is in combination with other features of OOP as are interfaces and hiding code.
Suppose, we have a customer (subscriber) in the library, which can borrow only one book for simplicity.

Interface of book:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

Next we can have many kind of books; one of type is fiction:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Now subscriber can have association to the book:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

All the three classes can be hidden for it's own implementation. Now we can use this code for DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

There are many different ways how to use dependency injection. It is possible to combine it with Singleton, etc., but still in basic it is only association realized by creating attribute of object type inside another object.
The usefulness is only and only in feature, that code, which we should write again and again is always prepared and done for us forward. This is why DI so closely binded with Inversion of Control (IoC) which means, that our program passes control another running module, which does injections of beans to our code. (Each object, which can be injected can be signed or considered as a Bean.) For example in Spring it is done by creating and initialization ApplicationContext container, which does this work for us. We simply in our code create the Context and invoke initialization the beans. In that moment injection has been done automatically.

年华零落成诗 2024-07-12 21:56:12

我会对依赖注入提出一个稍微不同的、简短而精确的定义,重点关注主要目标,而不是技术手段(遵循此处):

依赖注入是创建静态、无状态的过程
服务对象图,其中每个服务都由其参数化
依赖关系。

我们在应用程序中创建的对象(无论我们是否使用 Java、C# 或其他面向对象语言)通常属于两类之一:无状态、静态和全局“服务对象”(模块),以及有状态、动态和本地“数据对象”。

模块图(服务对象图)通常在应用程序启动时创建。 这可以使用容器(例如 Spring)来完成,但也可以通过将参数传递给对象构造函数来手动完成。 两种方法都有其优点和缺点,但在应用程序中使用 DI 绝对不需要框架。

一项要求是服务必须根据其依赖性进行参数化。 这到底意味着什么取决于给定系统中采用的语言和方法。 通常,这采用构造函数参数的形式,但使用 setter 也是一种选择。 这也意味着服务的依赖关系对服务的用户是隐藏的(当调用服务方法时)。

什么时候使用? 我想说,只要应用程序足够大,将逻辑封装到单独的模块中,并在模块之间使用依赖关系图,就可以提高代码的可读性和可探索性。

I would propose a slightly different, short and precise definition of what Dependency Injection is, focusing on the primary goal, not on the technical means (following along from here):

Dependency Injection is the process of creating the static, stateless
graph of service objects, where each service is parametrised by its
dependencies.

The objects that we create in our applications (regardless if we use Java, C# or other object-oriented language) usually fall into one of two categories: stateless, static and global “service objects” (modules), and stateful, dynamic and local “data objects”.

The module graph - the graph of service objects - is typically created on application startup. This can be done using a container, such as Spring, but can also be done manually, by passing parameters to object constructors. Both ways have their pros and cons, but a framework definitely isn’t necessary to use DI in your application.

One requirement is that the services must be parametrised by their dependencies. What this means exactly depends on the language and approach taken in a given system. Usually, this takes the form of constructor parameters, but using setters is also an option. This also means that the dependencies of a service are hidden (when invoking a service method) from the users of the service.

When to use? I would say whenever the application is large enough that encapsulating logic into separate modules, with a dependency graph between the modules gives a gain in readability and explorability of the code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文