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

#0098(2025/04/08) 依存性逆転の原則(Dependency Inversion Principle: DIP)

Posted at

依存性逆転の原則(Dependency Inversion Principle: DIP)

依存性逆転の原則(Dependency Inversion Principle: DIP)は、SOLID原則の中でも特にアーキテクチャ設計に直結する重要な概念です。DIPは以下の2つの要点を含んでいます:

  1. 高水準モジュールは低水準モジュールに依存すべきではない。両者は抽象に依存すべきである。
  2. 抽象は詳細に依存すべきでなく、詳細が抽象に依存すべきである。

本記事では、上級プログラマー向けにDIPを満たしていないコードと、それをDIP準拠の設計にリファクタした例を PythonTypeScript で紹介します。


DIPの本質

DIPは、実装(詳細)に依存するのではなく、抽象(インターフェース)に依存することで、モジュール間の結合度を下げ、拡張性やテスト容易性を高める設計原則です。

従来の自然な依存の向きを“意図的に逆転させる”のが正しい」という原則です。


Pythonによる例(ログ出力システム)

❌ DIPが守られていない例

class FileLogger:
    def log(self, message: str):
        with open('log.txt', 'a') as f:
            f.write(message + '\n')

class UserService:
    def __init__(self):
        self.logger = FileLogger()  # 具体クラスに依存

    def create_user(self, username):
        # ユーザー作成処理
        self.logger.log(f"User {username} created")

UserServiceFileLogger という低水準の実装に直接依存しており、拡張やテストが困難です。

✅ DIPが守られている例

from abc import ABC, abstractmethod

class Logger(ABC):
    @abstractmethod
    def log(self, message: str):
        pass

class FileLogger(Logger):
    def log(self, message: str):
        with open('log.txt', 'a') as f:
            f.write(message + '\n')

class ConsoleLogger(Logger):
    def log(self, message: str):
        print(message)

class UserService:
    def __init__(self, logger: Logger):  # 抽象に依存
        self.logger = logger

    def create_user(self, username):
        # ユーザー作成処理
        self.logger.log(f"User {username} created")

# 利用例
service = UserService(ConsoleLogger())
service.create_user("alice")
service = UserService(FileLogger())
service.create_user("beta")

UserServiceLogger という抽象に依存しているため、将来ログ出力方法を変更しても UserService 自体は影響を受けません。


TypeScriptによる例(通知システム)

❌ DIPが守られていない例

class EmailNotifier {
    send(message: string): void {
        console.log(`Sending email: ${message}`);
    }
}

class OrderService {
    private notifier = new EmailNotifier(); // 具体クラスに依存

    placeOrder(orderId: number): void {
        // 注文処理
        this.notifier.send(`Order ${orderId} has been placed.`);
    }
}

OrderServiceEmailNotifier に強く依存しており、他の通知方法への変更が難しい構造です。

✅ DIPが守られている例

interface Notifier {
    send(message: string): void;
}

class EmailNotifier implements Notifier {
    send(message: string): void {
        console.log(`Sending email: ${message}`);
    }
}

class SlackNotifier implements Notifier {
    send(message: string): void {
        console.log(`Sending Slack message: ${message}`);
    }
}

class OrderService {
    constructor(private notifier: Notifier) {} // 抽象に依存

    placeOrder(orderId: number): void {
        this.notifier.send(`Order ${orderId} has been placed.`);
    }
}

// 利用例
const service = new OrderService(new SlackNotifier());
service.placeOrder(123);

インターフェース Notifier によって OrderService と通知手段が疎結合になり、柔軟な差し替えやテストが可能になります。


まとめ

依存性逆転の原則を守ることで、アプリケーションのモジュールは実装の詳細ではなく、その**契約(インターフェース)**に依存する構造になり、保守性・再利用性・テスト容易性が劇的に向上します。上級開発者としては、常に "抽象との会話" を意識した設計を行うことが、堅牢なアーキテクチャ実現の鍵となります。

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