1

Introduction

In attempting to use Scalable Frontend 1 - Architecture Fundamentals inside said Dependency Injection time (dependency injection), feeling somewhat unclear, to find some information, it is more understand some, this translation record partially polymerized.

What is dependency injection

In software engineering, dependency injection is a technique in which an object receives other objects it depends on. These other objects are called dependencies. In a typical "use" relationship, the receiving object is called the client, and the delivered (ie, "injected") object is called the service. The code that passes the service to the client can be of many types, called an injector. The injector tells the client what service to use, rather than the client specifying which service to use. "Injection" refers to passing the dependency (service) to the object (client) that will use it.

The service is part of the client state. Passing the service to the client, rather than letting the client build or find the service, is the basic requirement of this pattern.

Advantage

  • Dependency injection allows the client to configure flexibly. Only the behavior of the client is fixed. The client can operate on anything that supports the internal interface that the client expects.
  • Dependency injection can be used to externalize the configuration details of the system into the configuration file, allowing the system to be reconfigured without recompiling. Separate configurations can be written for different situations where components require different implementations. This includes but is not limited to testing.
  • Because dependency injection does not require any changes to code behavior, it can be applied to refactoring legacy code. The result is that the client is more independent and easier to use stubs or mock objects for unit testing. This testability is usually the first benefit noticed when using dependency injection.
  • Dependency injection allows the client to remove all knowledge needed for a specific implementation. This helps isolate the client from the effects of design changes and defects. It enhances reusability, testability and maintainability.
  • Reduce boilerplate code in application objects, because all the work of initializing or setting dependencies is handled by the provider component.
  • Dependency injection allows parallel or independent development. Two developers can independently develop classes that use each other, and only need to know what interface the class will communicate through. Plug-ins are usually developed by third-party stores, and they never even communicate with developers who develop products that use plug-ins.
  • Dependency injection reduces the coupling between the class and its dependencies.

Disadvantage

  • The client created by dependency injection needs configuration details provided by the construction code. This can be burdensome when obvious defaults are available.
  • Dependency injection makes the code difficult to trace (read) because it separates behavior from construction. This means that developers must cite more files to track the execution of the system.
  • The dependency injection framework is implemented through reflection or dynamic programming. This hinders the use of IDE automation, such as "find references", "display call levels", and security refactoring.
  • Dependency injection usually requires more pre-development work, because you can't summon something to be the right thing when and where it is needed, but you must require it to be injected and ensure that it has been injected.
  • Dependency injection forces complexity from classes to connections between classes, which may not always be ideal or easy to manage.
  • Dependency injection may encourage dependencies based on dependency injection frameworks.

Several forms of dependency injection

There are at least three ways for client objects to receive references to external modules: constructor injection, setter injection, and interface injection.

Constructor injection

Dependencies are passed in through the parameters provided by the client's class constructor.

// Constructor
Client(Service service) {
    // Save the reference to the passed-in service inside this client
    this.service = service;
}

It is best to use it when all dependencies can be constructed first, because it can be used to ensure that the client object is always in a valid state, rather than making some of the dependency references null (not set). But on its own, it lacks the flexibility to change its dependencies in the future. This may be the first step in making the client immutable and thus thread safe.

// Constructor
Client(Service service, Service otherService) {
    if (service == null) {
        throw new InvalidParameterException("service must not be null");
    }
    if (otherService == null) {
        throw new InvalidParameterException("otherService must not be null");
    }

    // Save the service references inside this client
    this.service = service;
    this.otherService = otherService;
}

Setter injection

This approach requires the client to provide a setter for the dependency.

// Setter method
public void setService(Service service) {
    // Save the reference to the passed-in service inside this client.
    this.service = service;
}

The client is required to provide a setter for each dependency. In this way, you can freely manipulate the state of the dependency reference at any time. This provides flexibility, but if multiple dependencies are to be injected, it is difficult for the client to ensure that all dependencies are injected before they can be used.

Because these injections occur independently, it is impossible to determine when the injector has completed the connection with the client. The dependency may remain null just because the call to its setter fails. This will force a check to see if the injection from when the client is assembled to when it is used has been completed.

// Set the service to be used by this client
public void setService(Service service) {
    this.service = service;
}

// Set the other service to be used by this client
public void setOtherService(Service otherService) {
    this.otherService = otherService;
}

// Check the service references of this client
private void validateState() {
    if (service == null) {
        throw new IllegalStateException("service must not be null");
    }
    if (otherService == null) {
        throw new IllegalStateException("otherService must not be null");
    }
}

// Method that uses the service references
public void doSomething() {
    validateState();
    service.doYourThing();
    otherService.doYourThing();
}

Interface injection

This is just a setting method for the client to publish the role interface to the client's dependencies. It can be used to determine how the injector should talk to the client when injecting dependencies.

// Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

The advantage of interface injection is that dependencies can completely ignore their clients, but can still receive references to new clients and use it to send their own references back to the client. In this way, the dependency becomes an injector. The key is that the injection method (perhaps just a classic setter method) is provided through an interface.

Reference


XXHolic
363 声望1.1k 粉丝

[链接]