1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【オブジェクト指向】依存関係逆転の原則をわかりやすく

1
Posted at

はじめに

依存関係逆転の原則って、名前がかっこいいので言いたくなります。
ただ、理解が難しいのが難点です。

最初に見たとき、自分は「逆転」という言葉に引っ張られて、依存元と依存先が反対になることだと思っていました。しかし、そうではなかったようです。

しっくりきたのはDDDの勉強をしている時にこちらの本に書かれていた、具体的な実装に依存していたものが、抽象に依存するようになるという見方でした。

この記事では、この一点を中心に依存関係逆転の原則を整理してみます。

まずは逆転前の状態を見る

依存関係逆転の原則は、逆転後のきれいな形を見るより、先に逆転前を見るほうがわかりやすいです。

たとえば、注文処理をする OrderService が、メール通知クラス EmailNotifier を直接使っているコードを考えます。

BadExample.java
public class EmailNotifier {
    public void notify(String message) {
        System.out.println("メール送信: " + message);
    }
}

public class OrderService {
    private EmailNotifier notifier;

    public OrderService() {
        this.notifier = new EmailNotifier();
    }

    public void placeOrder(String item) {
        System.out.println(item + "を注文しました");
        notifier.notify(item + "の注文が完了しました");
    }
}

このコードで見たいのは、OrderServiceEmailNotifier を知ってしまっていることです。

OrderService が本当に必要としているのは「通知する機能」のはずなのに、実際には「メールで通知するクラス」に依存しています。つまり、依存先が最初から具体的な実装です。

この状態だと、メールをSMSに変えたくなったときも、テストで通知処理だけ差し替えたいときも、OrderService 側に影響が出ます。上位の処理が下位の実装詳細に引っ張られてしまうわけです。

上位:ユーザーに近い抽象的な処理
下位:コンピュータに近い具体的な処理

抽象に依存させると景色が変わる

では、OrderService が必要としているものをそのまま抽象にしてみます。

ここで大事なのは、「メール送信」という具体的な手段ではなく、「通知できること」を切り出すことです。

GoodExample.java
public interface Notifier {
    void notify(String message);
}

public class EmailNotifier implements Notifier {
    @Override
    public void notify(String message) {
        System.out.println("メール送信: " + message);
    }
}

public class SmsNotifier implements Notifier {
    @Override
    public void notify(String message) {
        System.out.println("SMS送信: " + message);
    }
}

public class OrderService {
    private Notifier notifier;

    public OrderService(Notifier notifier) {
        this.notifier = notifier;
    }

    public void placeOrder(String item) {
        System.out.println(item + "を注文しました");
        notifier.notify(item + "の注文が完了しました");
    }
}

こうすると、OrderService が依存している相手は EmailNotifier ではなく、Notifier です。

OrderService からすると、メールなのかSMSなのかはどうでもよくなります。必要なのは「通知できること」だけ。だから、実装の差し替えがかなり素直になります。

「逆転」の正体はここだった

自分がいちばん引っかかったのはここでした。

逆転前は、こうです。

OrderService → EmailNotifier

OrderService は、下位モジュールの具体クラスに依存しています。

逆転後は、こうなります。

OrderService → Notifier ← EmailNotifier

この形になると、OrderService が見ているのは具体クラスではなく抽象です。そして EmailNotifier のような下位モジュール側が、その抽象に合わせて実装する立場になります。

つまり、逆転したのは「処理の流れ」そのものではなく、具体に向いていた依存が、抽象に向くようになったということです。

対義語になっていることから逆転したのがわかりやすいです。

まとめ

依存関係逆転の原則を、自分は最初「矢印が逆向きになる話」だと思っていました。ですが、もっとシンプルに考えると、

具体的な実装に依存していたものを、抽象に依存させる。

ということでした。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?