6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

からくりAdvent Calendar 2019

Day 8

DIについてメモ

Posted at

DIを調べると、、、

DI 【 Dependency Injection 】 依存性注入

DIとは、コンピュータプログラムのデザインパターンの一つで、オブジェクトなどの間に生じる依存関係をオブジェクト内のコードに直接記述せず、外部から何らかの形で与えるようにする手法。

[参考]http://e-words.jp/w/DI.html

依存性注入(Dependency Injection)とは?

  • Dependencyとは

    • サービスとして使用できるオブジェクトのこと。
  • Injectionとは

    • オブジェクト(サービス)を、それを使うオブジェクト(クライアント)に渡すこと。

ということで上記をまとめると

あるオブジェクト(サービス)を別のオブジェクト(クライアント)に渡すことを依存性注入という

DIのメリット・デメリット

  • メリット

    • クラス同士を疎結合にできる、独立性を高められる
    • クラスの単体テストがやりやすくなる
    • クラスの再利用性が向上する
  • デメリット(DIのデメリットというかDIフレームワークのデメリット。。。)

    • はじめにクラスを沢山作るので工数がかかる場合が多い
    • プログラムの実行スピードが遅くなる可能性が高い
    • 学習コストがかかる

[参考]https://blog.shin1x1.com/entry/di-memo

DIのパターン

非DI:コンストラクタを直接呼び出す

Car.java
class Car {
    Engine engine;
    Wheels wheels;

    Car() {
        this.engine = new Engine();
        this.wheels = new Wheels();
    }
}

CarクラスにEngineクラス、Wheelsクラスが必要な場合に、コンストラクタ内でインスタンス化すると密結合になってしまい、EngineクラスとWheelsクラスが完成しないとCarクラスの実装、テストが進められない。という事態になる。。。

DI①:コンストラクタの引数を利用したDI

Car.java
class Car {
    Engine engine;
    Wheels wheels;

    Car(Engine engine, Wheels wheels) {
        this.engine = engine;
        this.wheels = wheels;
    }
}

Car car = new Car(new Engine(), new Wheels())

外からインスタンス化したEngineクラス、Wheelsクラスを渡しているため、Carクラス内でインスタンス化するよりも疎結合になってます。
クラスを変更したいときはコンストラクタに渡す引数を変更すれば良いのでテストもしやすくなってます。

→ ただ、new地獄になってしまう可能性があります。また、クラスを変更する際に書き換える作業があリマス。。。

DI②:Factoryクラス

Factory.java
class Factory {
    public Engine getEngine() {
        return new DieselEngine();
    }
    public Wheels getWheels() {
        return new SteelWheels();
    }
}

Engine、Wheelsのインターフェースを作成

Engine.java
interface Engine {}
Wheels.java
interface Wheels {}

Engine、Wheelsインターフェースを実装したDieselEngineクラスとSteelWheelsクラスを作成

DieselEngine.java
class DieselEngine implements Engine {}
SteelWheels.java
class SteelWheels implements Wheels {}

Car car = new Car(factory.getEngine(), factory.getWheels());

Factoryクラスを作成することで管理しやすくなり、また、Engineインターフェース、Wheelsインタフェースを挟むことでクラスの差し替えが容易になりました。

→ デメリットとしては、多くのクラスからオブジェクト生成を委譲されるためFactoryクラスが大きくなりすぎる可能性があります。また、Factory地獄になる可能性も。。。

DI③:Dagger2

最後にAndroidのDIフレームワークの1つであるDagger2でコンストラクタインジェクション(Constructor Injection)の簡単な実装例を紹介して終わりたいと思います。

使用するアノテーションを簡単に説明

  • @Inject
    • 依存性を注入したいオブジェクトへ付与する
    • @Injectアノテーションが付与されたコンストラクタを使ってインスタンスを生成する
  • @Component
    • 必要なインスタンスを渡してくれるインターフェース
    • 注入される側はComponent経由でインスタンスをもらう

Dagger2は@Injectアノテーションが付与されたコンストラクタを使ってインスタンスを生成します。
なので、Carクラスのコンストラクタに@Injectアノテーションを付与します。

Car.java
class Car {
    Engine engine;
    Wheels wheels;

    @Inject
    Car(Engine engine, Wheels wheels) {
        this.engine = engine;
        this.wheels = wheels;
    }

    void drive() { ... }
}

上記と同様の理由でEngineクラス、Wheelsクラスのコンストラクタにも@Injectアノテーションを付与します。

Engine.java
class Engine {
    @Inject
    Engine(){}
}
Wheels.java
class Wheels {
    @Inject
    Wheels(){}
}

Carインスタンスを渡してくれるインターフェースを作成します。
→ 必要なインスタンスを渡してくれる@Componentアノテーションを付与します。

CarComponent.java
@Component
interface CarComponent {
    Car getCar();
}

MainActivityです。
CarComponentインターフェースを実装したDaggerCarComponentからインスタンス化します。
その後、component.getCar()にて、Carクラスを受け取ります。

MainActivity.java
class MainActivity extends AppCompatActivity {
    Car car;
    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CarComponent component = DaggerCarComponent.create(); ココ
        car = component.getCar();
        car.drive();
    }
}

Dagger2が自動生成してくれるソースで、先ほど定義したCarComponentインターフェースを実装してます。

DaggerCarComponent.java
pubic final class DaggerCarComponent implements CarComponent {
    private DaggerCarComponent(){}

    public static Builder builder() {
        return new Builder();
    }

    public static CarComponent create() {
        return new Builder().build();
    }

    @Override 
    public Car getCar() {
        return new Car(new Engine(), new Wheels());
    }

    public static final class Builder {
        private Builder() {}

        public CarComponent build() {
            return new DaggerCarComponent();
        }
    }
}

以上です。

[参考]
http://blog.a-way-out.net/blog/2015/08/31/your-dependency-injection-is-wrong-as-I-expected/
http://yuki312.blogspot.com/2016/03/android-dagger2.html

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?