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

DIをなぜ使うのかAIと問答しながら回答を探ってみた

Posted at

Q. そもそもDIってなに?

A. DI(Dependency Injection:依存性注入)とは、コンポーネントやクラスが外部からその必要な依存関係(他のクラスやサービスなど)を受け取る設計パターンです。

Q. わかりづらいから童話調で説明して?

A.
むかしむかし、あるプログラムの王国がありました。
『依存の王国のものがたり』
ある日、ユーザーサービス王子は、データベース姫と仲良く暮らしていました。

// むかしのコード
class ユーザーサービス王子 {
  private データベース姫 = new データベース姫();  // 強い結びつき
  
  ユーザーを探す(id: string) {
    return this.データベース姫.探す(id);
  }
}

でも、これには大きな問題がありました。ユーザーサービス王子は、データベース姫なしでは何もできなかったのです。テストの妖精が来ても、データベース姫を置き換えることができず、とても困っていました。
そこで、賢者のDI(依存性の注入)さんが現れ、こう言いました。
「これからは、王子様とお姫様は、もっと自由な関係になりましょう。お互いを直接知らなくても、誰かが引き合わせてくれれば良いのです」
そして、賢者のDIさんは、こんな魔法をかけました:

// 新しい魔法のコード
interface データベースの約束 {
  探す(id: string): Promise<any>;
}

class ユーザーサービス王子 {
  // 誰でも良いから、約束を守れる人を待つ
  constructor(private データベース: データベースの約束) {}
  
  ユーザーを探す(id: string) {
    return this.データベース.探す(id);
  }
}

class データベース姫 implements データベースの約束 {
  探す(id: string) {
    return Promise.resolve({ id, name: 'はなこ' });
  }
}

// テストの妖精さん
class テストデータベース implements データベースの約束 {
  探す(id: string) {
    return Promise.resolve({ id, name: 'てすとさん' });
  }
}

そして、王国の人々は、こんな風に暮らすようになりました:

// 普段の暮らし
const データベース = new データベース姫();
const 王子様 = new ユーザーサービス王子(データベース);

// テストの日
const テストデータ = new テストデータベース();
const テスト用の王子様 = new ユーザーサービス王子(テストデータ);

この魔法のおかげで:

王子様は、誰が来ても対応できるようになりました
テストの妖精さんも、簡単にテストができるようになりました
みんなが自由に、でも約束を守って暮らせるようになりました

DIの魔法によって、プログラムの王国はより柔軟で、テストしやすく、幸せな場所になりました。
めでたし、めでたし。

おまけ:王国の新しい規則

// 王国のDIコンテナ
class 王国のルール {
  private お供たち = new Map();

  仲間を増やす(名前: string, 仲間: any) {
    this.お供たち.set(名前, 仲間);
  }

  仲間を呼ぶ(名前: string) {
    return this.お供たち.get(名前);
  }
}

const 王国 = new 王国のルール();
王国.仲間を増やす('データベース', new データベース姫());
王国.仲間を増やす('ユーザーサービス', 
  new ユーザーサービス王子(王国.仲間を呼ぶ('データベース')));

こうして、王国の皆は、お互いを思いやりながら、でも自由に暮らせるようになりました。これこそが、DIの魔法がもたらした幸せなのです。

Q. 「これからは、王子様とお姫様は、もっと自由な関係になりましょう。お互いを直接知らなくても、誰かが引き合わせてくれれば良いのです」という言葉の意味がわからない

A.
この部分を、より具体的に説明しましょう。

昔の関係(DIを使わない場合):

// 昔の王子様は、必ずデータベース姫と一緒じゃないといけなかった
class ユーザーサービス王子 {
  // 王子様が直接データベース姫を呼び出している(強い結びつき)
  private データベース姫 = new データベース姫();
  
  ユーザーを探す() {
    // データベース姫しか選べない!
    return this.データベース姫.探す();
  }
}

// 使う時
const 王子様 = new ユーザーサービス王子();
// データベース姫以外を使うことができない!

これは、まるで王子様がデータベース姫としか結婚できないような状態です。テストをしたいときも、他のデータを使いたいときも、必ずデータベース姫を使わないといけません。

新しい関係(DIを使う場合):

// 約束(インターフェース)を作る
interface データベースの約束 {
  探す(): Promise<any>;
}

class ユーザーサービス王子 {
  // 王子様は「データベースの約束」を守れる人なら誰でもOK!
  constructor(private データベース: データベースの約束) {}
  
  ユーザーを探す() {
    // 渡されたデータベースを使う(柔軟な関係)
    return this.データベース.探す();
  }
}

// 本番のデータベース姫
class データベース姫 implements データベースの約束 {
  探す() {
    return Promise.resolve({ name: 'はなこ' });
  }
}

// テスト用のデータベース妖精
class テストデータベース implements データベースの約束 {
  探す() {
    return Promise.resolve({ name: 'てすとさん' });
  }
}

// 使う時
// 本番では
const 本番用の相手 = new データベース姫();
const 王子様1 = new ユーザーサービス王子(本番用の相手);

// テストでは
const テスト用の相手 = new テストデータベース();
const 王子様2 = new ユーザーサービス王子(テスト用の相手);

つまり:

1.昔は王子様とデータベース姫が強く結びついていて離れられなかった
2.新しい方法では、「データベースの約束」を守れる人なら誰でも王子様と協力できる
3.誰かが王子様に「この人と一緒に仕事してね」と紹介する(これが依存性の注入)

実際の例で言うと:

// 昔の方法(固定的な関係)
class レストラン {
  private コック = new 日本料理コック();  // 日本料理しか作れない!
}

// 新しい方法(柔軟な関係)
interface 料理を作れる {
  料理を作る(): string;
}

class レストラン {
  constructor(private コック: 料理を作れる) {}  // どんなコックでもOK!
}

// いろんなコックを使える
const 和食コック = new 日本料理コック();
const 中華コック = new 中華料理コック();

const 和食レストラン = new レストラン(和食コック);
const 中華レストラン = new レストラン(中華コック);

このように、DIを使うことで:

1.より柔軟な関係が作れる
2.テストが簡単になる
3.コードの再利用がしやすくなる

という利点があります。これが「自由な関係」の意味です

なんとなくわかったこと

DIは、クラスやコンポーネントが必要なものを自分で作るのではなく、外から受け取る方式である。
これにより、コードの部品を簡単に付け替えられるようになり(テストがしやすくなり)、柔軟性が高まる。
たとえば、レストランクラスが特定のコックを直接作るのではなく、外から任意のコックを受け取れるようにする、というイメージ。

結論

最後のコックの例えが一番わかりやすかったな……

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