依存関係逆転(DIP: Dependency Inversion Principle)メモ

  • 前提:
    • 「不安定→安定」の依存になっている場合は、原則通りなのでOK。
      • 個別コードが、ライブラリを呼び出す場合など。
    • アプリケーションによっては、「安定→不安定」の依存になる場合がある。
  • 課題:
    • フレームワーク側(下位層)が個別側(上位層)のインスタンスを知るにはどうすればいい?
      1. リフレクションで引き当てる
      2. コールバッククラスの登録

「2. コールバッククラスの登録」を例にしたサンプル:

フレームワークレイヤ

// コールバックされるインターフェース
interface CallbackableIF {
    int x;
    int y;
    void calc(); // ←ここの実装内容が、上位層に依存するとする。
}
// メソッドの呼び口を用意するけど、その具体的な実装内容は上位にお任せする
class Callbacker {
    public void callback(CallbackableIF callbackableImpl) {
        callbackableImpl.calc();
    }
}

↑ここまでフレームワーク(下位層)
↓ここからユーザ側の個別コード(上位層)

■具象レイヤ

// 上記インターフェースの実装その1
class CallbackImplA implements CallbackableIF {
    public CallbackImplA(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void calc() {
        System.out.println(x + y); // 足し算で実装
    }
}
// 上記インターフェースの実装その2
class CallbackImplB implements CallbackableIF {
    public CallbackImplB(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void method() {
        System.out.println(x * y); // 掛け算で実装
    }
}

■設定(実行)レイヤ

public class CallbackSample {
    public static void main(String args[]) {
        int x = 10;
        int y = 20;
        CallbackableIF callbackableImplA = new CallbackImplA(x, y);
        CallbackableIF callbackableImplB = new CallbackImplB(x, y);

        Callbacker callbacker = new Callbacker();
        callbacker.callback(callbackableImplA);
        callbacker.callback(callbackableImplB);
    }
}

UML

[Initializer]──<<create>>──→[CallbackImpl]……|>[Callbackable]
   │                          │
   └────<<create>>───→[Framework]◆──────┘
  • (上位) → 設定レイヤ → 具象レイヤ → フレームワークレイヤ → (下位)
  • フレームワークレイヤは、Callbackable の実装を上位にお任せする。
    • Runnable の run が好例。
  • 下位レイヤから上位レイヤへの循環参照を防ぐための王道パターン