9歳娘「パパ、具体じゃなくインターフェースに依存して？」

# 2112年、ある日の我が家

ワイ「ぐぅっ！肝臓がァッ！」

娘「パパ、大丈夫！？」

ワイ「さ、酒の飲み過ぎで肝臓がァッ！」

娘「大変！」
娘「パパの肝臓って**人工臓器**だよね！？」
娘「新しい人工肝臓に交換したら！？」

ワイ「う〜ん・・・それがな？」
ワイ「ワイの肝臓は、**株式会社ISON**っていうメーカーはんの製品なんやけど」
ワイ「ISON社の人工臓器って、**独自規格**なんよ」
ワイ「せやから、他のメーカーの人工臓器とは**接続端子**の形状が違ってて」
ワイ「付け替えることができひんのや・・・」

娘「え・・・、そうなの？」
娘「最近では、どこのメーカーの人工臓器も」
娘「**DS-1規格**っていう世界的な規格に則って作られているから」
娘「付け替えできるって聞いたことがあるけど・・・」

ワイ「ワイの人工肝臓は、古いやつやから独自規格なんや・・・」

娘「じゃあ、早くISON社の人工肝臓を買ってこないと！」

ワイ「ISON社は、3年前に倒産したわ・・・」
ワイ「**倒産**したから、**父さん**の肝臓はもうダメや・・・」

娘「ああっ、早めに肉体をリファクタして」
娘「世界的なDS-1規格に対応した人工肝臓に換えておけば良かったのに・・・」

ワイ「せやな・・・**具体的なメーカーの肝臓**に依存するんじゃなくて」
ワイ「**肝臓として働くもの**、という**規格**」
ワイ「つまり**インターフェース**に依存すべきだった、ってことやな・・・」

娘「そうだね・・・」
娘「そうしておけば、メーカーが倒産しても大丈夫だった」
娘「だって、他のメーカーの人工臓器に付け替えられるもの・・・」

ワイ「なるほどなぁ・・・」
ワイ「**疎結合**っていうやつやな・・・」

# 目が覚め、現代

ワイ「ハッ！」
ワイ「夢か・・・」
ワイ「でも何か、大切なことに気づかされたような・・・」

娘「パパ、やっと起きたの？」
娘「もうお仕事の時間だよ！」

ワイ「おお、ほんまや！」

# リモートワーク開始

ワイ「さて今日は、メール通知機能を開発していくでぇ」
ワイ「ユーザーさんからの注文が入ったら、お店にメールでお知らせするんや」

```typescript:TypeScript
// ユーザーからの注文を処理するクラス
class OrderController {
    private emailClient: EmailClient;

    constructor() {
        // メール送信ライブラリに直接依存している
        this.emailClient = new EmailClient();
    }

    /**
     * 途中省略
     */

    // 通知メソッド
    sendNotification(userId: number, order: Order): void {
        // メール通知を送信
        this.emailClient.sendEmail({
          to: "shop@example.com",
          subject: `注文が入りました`,
          body: /* 省略 */,
        });
    }
}
```

ワイ「ん・・・？」
ワイ「これって・・・」
ワイ「なんか、夢で見た**ワイの肝臓**に似てるな・・・」
ワイ「**メール**という**具体的な通知方法**に依存してしまってるやん」
ワイ「もし、クライアントはんから──」

> 「やっぱり、メールではなく**LINEE**で通知してほしい」

ワイ「なんて言われてしまった場合に」
ワイ「この`EmailClient`を使っている箇所を、**全て書き換えなアカン**・・・」
ワイ「その場合、コードの修正量が多くなりそうやなぁ」

ワイ「ほな、夢で学んだように」
ワイ「**インターフェース**に依存するように書き直してみよか」
ワイ「まずは、通知の規格を定義したインターフェースを作るでぇ」
ワイ「通知をする時に必要なのは、ユーザー情報と、タイトルと、通知内容やから──」

```typescript:TypeScript
// 通知の規格を定義するインターフェース
interface NotificationInterface {
    send(user: User, title: string, message: string): void;
}
```
ワイ「↑こうやな！」
ワイ「これが、人工臓器でいう**DS-1規格**みたいなもんやな」
ワイ「通知機能というものは、この規格に従わなアカンで〜！っていう感じや」

娘「へぇ〜」
娘「じゃあ、メール通知機能は、その`NotificationInterface`に則って作るの？」

ワイ「せやで！」
ワイ「こんな感じや」

```typescript:TypeScript
// メール通知
class EmailNotification implements NotificationInterface {
    private emailClient: EmailClient;

    constructor() {
        // メールクライアントを内部で保持
        this.emailClient = new EmailClient();
    }

    // メールクライアントのsendEmail()を、
    // send()メソッドでラップする
    send(user: User, title: string, message: string): void {
        this.emailClient.sendEmail({
            to: user.email,
            subject: title,
            body: message
        });
    }
}
```

娘「そっか」
娘「さっき決めたインターフェースに従って」
娘「メール送信ライブラリをラップしてやるんだね！」

ワイ「その通りや」
ワイ「そして、さっきの`OrderController`も、このインターフェースを使うように書き直すんや」

```typescript:TypeScript
class OrderController {
    private notification: NotificationInterface;

    // コンストラクタで NotificationInterface を受け取る
    constructor(notification: NotificationInterface) {
        this.notification = notification;
    }

    sendNotification(userId: number, order: Order): void {
        // インターフェースで定義されている send() を呼ぶ
        this.notification.send(user, title, message);
    }
}
```

娘「へぇ〜！」
娘「ここでは具体的な通知方法は分からないけど」
娘「とにかく、通知の規格に従ってメッセージを送信するんだね！」

ワイ「せやで」
ワイ「そんで、`OrderController`を`new()`するときに」
ワイ「`EmailNotification`のインスタンスを渡してやるんや」

```typescript:TypeScript
const orderController = new OrderController(new EmailNotification());
```

娘「なるほど〜」
娘「クラスを`new()`する時に、具体的な通知方法を指定してやるんだね」
娘「**依存性の注入**ってやつだね」

ワイ「おお、よう知っとるな」

娘「てへへ」
娘「これなら確かに、具体に依存していない気がするね」
娘「でも、実際に他の通知方法に切り替えたくなった場合にはどうすればいいの？」
娘「たとえば、LINEE通知に切り替えたくなったら？」

ワイ「おう、その場合はやな？」
ワイ「今度は、LINEE通知のライブラリを、`NotificationInterface`に合わせてラップしてあげるんや」

```typescript:TypeScript
// LINEE通知
class LineeNotification implements NotificationInterface {
    private lineeSDK: LineeSDK;

    constructor() {
        // LINEEのSDKを内部で保持
        this.lineeSDK = new LineeSDK();
    }

    // LINEE SDKが持つsendMessage()を、
    // send()メソッドでラップする
    send(user: User, title: string, message: string): void {
        this.lineeSDK.sendMessage(user.lineeId, title, message);
    }
}
```

ワイ「↑こんな感じや！」

娘「そっか」
娘「人工臓器で例えると」
娘「色々なメーカーが、DS-1規格に合わせて製品を作るみたいな感じだね」

ワイ「その通りやでぇ」
ワイ「そして、`OrderController`を`new()`するときに」
ワイ「`LineeNotification`のインスタンスを渡すように修正するんや」

```typescript:TypeScript
const orderController = new OrderController(new LineeNotification());
```

ワイ「これで、メール通知もLINEE通知も」
ワイ「サクッと切り替えられるようになったで」

娘「へぇ〜」
娘「だいぶ差し替えやすくなったね」
娘「インターフェースが同じだから！」

ワイ「せやせや」
ワイ「いわゆる**疎結合**ってやつや」
ワイ「メール送信ライブラリと**密結合**になってると」
ワイ「ライブラリを**剥がしたい時**に大変やからな」

娘「なるほどね〜」

# まとめ

#### 具体的な実装に依存すると、後からの変更が難しくなる

- 人工臓器の例
    - ISON社の独自規格だと、他のメーカーの製品に付け替えるのが大変
- コードの例
    - `EmailClient`に直接依存すると、他の通知方法に変更する場合のコストが高い

#### インターフェースに依存することで、交換が容易になる

- 人工臓器の例
    - DS-1規格に則っていれば、どのメーカーの製品でも使える
- コードの例：
    - `NotificationInterface`を実装していれば、どの通知方法でも使える

娘「↑こういうことだね！」

ワイ「そうや！」

娘「通知方法を変更しやすいのもあるけど」
娘「テストコードも書きやすくなりそうだね！」

ワイ「へ？そうなん？」

娘「うん！」
娘「テストの時には、実際の通知方法を使わないで」
娘「通知の規格に従ったモッククラスを渡せばいいんだよ！」

ワイ「おお〜、そうやな！」
ワイ「ワイはテストコードなんて書かへんから、関係ないけどな！」

よめ太郎「いや書かんかい」

**〜おしまい〜**

## 追記
※普通に肝臓は大事にしましょう。休肝日を作りましょう。

## 新しい記事もよろしくやで！
- [9歳娘「パパのコンポーネント、条件だらけで大変だね！」](https://qiita.com/Yametaro/items/59852ca94ead15c77741)