Revision 2 as of 2011-07-10 01:27:55

Clear message
Locked History Actions

DI

DI

wikipediaの2011/7/10時点の翻訳(予定)。

オブジェクト指向コンピュータプログラミングにおける依存性注入(DI)とは、プログラムの部分が別の部分を使用することを示す、つまり外部依存や参照をソフトウェアコンポーネント供給するものである。 簡単に言えば、依存性注入とは、依存をソフトウェアコンポーネントに渡したり設定したりすることである。 技術的には振る舞いと依存解決とを分離することにより、これにより強依存のコンポーネントを分割する。 コンポーネント自体が依存を要求する代わりに、それらは与えられる、あるいは注入される。

開発者はコンポーネント間の依存を様々な理由で減らそうと努力するが、これは新たな問題を引き起こす。 コンポーネントは、その目的を達成するのに必要なほかのコンポーネントをどうやって知ることができるのか?

伝統的な解決策は依存をハードコードすることである。 例えば、データベースドライバを必要になると、コンポーネントは特定のドライバをロードするコードを実行し、それを設定し、データベースとのやりとりのために必要なメソッドを呼び出す。もし別のデータベースを扱う必要が出てくれば、このコードは変更されるか、あるいはもっと悪い場合はコピーされて変更される(これはDRY原則に反する)。

依存性注入がこれを解決する。 依存をハードコードする代わりに、単にコンポーネントは必要なサービスをリストし、DIフレームワークがそれを供給するというものである。

At runtime, an independent component will load and configure the database driver and offer a standard interface to interact with the database. Again, the details have been moved from the original component to a set of new, small, database-specific components, reducing the complexity of them all. In DI terms, these new components are called "service components", as they render a service (database access) for one or more other components.

Dependency injection is a specific form of inversion of control, where the concern being inverted is the process of obtaining the needed dependency. The term was first coined by Martin Fowler to describe the mechanism more clearly.[1]

目次

    1 Basics
    2 Code illustration using Java
        2.1 Highly coupled dependency
        2.2 Manually injected dependency
        2.3 Framework-managed dependency injection
    3 Benefits and drawbacks
    4 Types
    5 References
    6 See also
    7 External links

基本

Without dependency injection, a consumer component that needs a particular service in order to accomplish a task will depend not only on the interface of the service, but on the details of a particular implementation of it as well. The user component has to handle both its use and the life cycle of that service - creating an instance, opening and closing streams, disposing of unneeded objects, etc.

Using dependency injection, however, the life-cycle of a service is handled by a dependency provider rather than the consumer. The dependency provider is an independent, external component that links the consuming component and the providing component. The consumer would thus only need a reference to an implementation of the service that it needed in order to accomplish the necessary task.

Such a pattern involves at least three elements:

  • a dependent consumer, the definition of its service dependencies, and an injector (sometimes referred to as a provider or container).

The dependent is a consumer component that needs to accomplish a task in a computer program. In order to do so, it needs the help of various services (the dependencies) that execute certain sub-tasks. The provider is the component that is able to compose the dependent and its dependencies so that they are ready to be used, while also managing these objects' life cycles. The provider may be implemented, for example, as a service locator, an abstract factory, a factory method, or a more complex abstraction such as a framework.

The following is an example. A car (the consumer) depends upon an engine (the dependency) in order to move. The car's engine is made by an automaker (the dependency provider). The car does not know how to install an engine into itself, but it needs an engine in order to move. The automaker installs an engine into the car and the car utilizes the engine to move.

When the concept of dependency injection is used, it decouples high-level modules from low-level services. The result is called the dependency inversion principle. [edit] Code illustration using Java

Using the car/engine example mentioned above, the following Java examples show how coupled (manually-injected) dependencies and framework-injected dependencies are typically staged.

public interface ICar {

  • public float getSpeed(); public void setPedalPressure(final float PEDAL_PRESSURE);

}

public interface IEngine {

  • public float getEngineRotation(); public void setFuelConsumptionRate(final float FUEL_FLOW);

}

[edit] Highly coupled dependency

The following shows a common arrangement with no dependency injection applied:

public class DefaultEngineImpl implements IEngine {
    private float engineRotation = 0;
 
    public float getEngineRotation() {
        return engineRotation;
    }
 
    public void setFuelConsumptionRate(final float FUEL_FLOW) {
        engineRotation = …;
    }
}
 
public class DefaultCarImpl implements ICar {
    private IEngine engine = new DefaultEngineImpl();
 
    public float getSpeed() {
        return engine.getEngineRotation()*…;
    }
 
    public void setPedalPressure(final float PEDAL_PRESSURE) {
        engine.setFuelConsumptionRate(…);
    }
}
 
public class MyApplication {
    public static void main(String[] args) {
        ICar car = new DefaultCarImpl();
        car.setPedalPressure(5);
        float speed = car.getSpeed();
        System.out.println("Speed of the car is " + speed);
    }
}

In the above example, using the ICar interface, an engine instance is created by using the IEngine interface in order to perform operations on the car. Hence, it is considered highly-coupled, because it couples a car directly with a particular engine implementation.

In cases where the DefaultEngineImpl dependency is managed outside of the scope of the car, the class of the car implementing the ICar interface must not instantiate the DefaultEngineImpl dependency. Instead, that dependency is injected externally. [edit] Manually injected dependency

Refactoring the above example to use manual injection:

public class DefaultCarImpl implements ICar {
    private IEngine engine;
 
    public DefaultCarImpl(final IEngine engineImpl) {
        engine = engineImpl;
    }
 
    public float getSpeed() {
        return engine.getEngineRotation()*…;
    }
 
    public void setPedalPressure(final float PEDAL_PRESSURE) {
        engine.setFuelConsumptionRate(…);
    }
}
 
public class CarFactory {
    public static ICar buildCar() {
        return new DefaultCarImpl(new DefaultEngineImpl());
    }
}
 
public class MyApplication {
    public static void main(String[] args) {
        ICar car = CarFactory.buildCar();
        car.setPedalPressure(5);
        float speed = car.getSpeed();
        System.out.println("Speed of the car is " + speed);
    }
}

In the example above, the CarFactory class assembles a car and an engine together by injecting a particular engine implementation into a car. This moves the dependency management from the DefaultCarImpl class into the CarFactory class. As a consequence, if the DefaultCarImpl needed to be assembled with a different DefaultEngineImpl implementation, the DefaultCarImpl code would not be changed.

In a more realistic software application, this may happen, if a new version of a base application is constructed with a different service implementation. Using factories, only the service code and the factory code would need to be modified, but not the code of the multiple users of the service. However, this still may not be enough abstraction for some applications, since in a realistic application there would be multiple factory classes to create and update. [edit] Framework-managed dependency injection

There are several frameworks available that automate dependency management through delegation. Typically, this is done with a container using XML or metadata definitions. Refactoring the above example to use an external XML-definition framework:

    <service-point id="CarBuilderService">
        <invoke-factory>
            <construct class="Car">
                <service>DefaultCarImpl</service>
                <service>DefaultEngineImpl</service>
            </construct>
        </invoke-factory>
    </service-point>

/** Implementation not shown **/
 
public class MyApplication {
    public static void main(String[] args) {
        Service service = (Service)DependencyManager.get("CarBuilderService");
        ICar car = (ICar)service.getService(Car.class);
        car.setPedalPressure(5);
        float speed = car.getSpeed();
    }
}

In the above example, a dependency injection service is used to retrieve a CarBuilderService service. When a car is requested, the service returns an appropriate implementation for both the car and its engine.

As there are many ways to implement dependency injection, only a small subset of examples is shown herein. Dependencies can be registered, bound, located, externally injected, etc., by many different means. Hence, moving dependency management from one module to another can be accomplished in many ways.

メリットとデメリット

One benefit of using the dependency injection approach is the reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component.[2]

Another benefit is that it offers configuration flexibility because alternative implementations of a given service can be used without recompiling code. This is useful in unit testing, as it is easy to inject a fake implementation of a service into the object being tested by changing the configuration file.

However, there should exist a definite reason for moving a dependency away from the object that needs it. One drawback is that excessive or inappropriate use of dependency injection can make applications more complicated, harder to understand, and more difficult to modify. Code that uses dependency injection can seem magical to some developers, since instantiation and initialization of objects is handled completely separately from the code that uses them. This separation can also result in problems that are hard to diagnose. Additionally, some dependency injection frameworks maintain verbose configuration files, requiring that a developer understand the configuration as well as the code in order to change it. For example, suppose a Web container is initialized with an association between two dependencies and that a user who wants to use one of those dependencies is unaware of the association. The user would thus not be able to detect any linkage between those dependencies and hence might cause drastic problems by using one of those dependencies.

Another drawback is that some IDEs might not be able to accurately analyze or refactor code when configuration is "invisible". Some IDEs mitigate this problem by providing explicit support for various frameworks. Additionally, some frameworks provide configuration using the programming language itself, allowing refactoring directly. Other frameworks, such as the Grok Web framework, introspect the code and use convention over configuration as an alternative form of deducing configuration information. For example, if a Model and View class were in the same module, then an instance of the View will be created with the appropriate Model instance passed into the constructor. [edit] Types

Fowler identifies three ways in which an object can get a reference to an external module, according to the pattern used to provide the dependency:[3]

  • Type 1 or interface injection, in which the exported module provides an interface that its users must implement in order to get the dependencies at runtime. Type 2 or setter injection, in which the dependent module exposes a setter method that the framework uses to inject the dependency. Type 3 or constructor injection, in which the dependencies are provided through the class constructor.

It is possible for other frameworks to have other types of injection, beyond those presented above.[4] [edit] References