2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonにおける依存性注入(DI)の設計:拡張性とテスタビリティの両立

Posted at

概要

**依存性注入(Dependency Injection, DI)**は、
オブジェクトの依存対象(サービス、設定、外部リソースなど)を
内部で生成せず、外部から注入する設計手法である。

これは「疎結合」「テスタビリティ」「再利用性」「設定柔軟性」など、
ソフトウェア品質において不可欠な設計戦略であり、
Pythonでも実用的に導入・応用可能である。


1. なぜDIが必要か?

❌ 依存を内部でハードコード

class Service:
    def __init__(self):
        self.repo = FileRepository()

再利用不可、モック困難、変更不能な密結合コード


✅ 外部から依存を注入

class Service:
    def __init__(self, repo):
        self.repo = repo

→ 依存対象の制御権が呼び出し元に移る
柔軟性・テスト容易性・動的切替が可能


2. DIの3つの形態

タイプ 概要
コンストラクタ注入 __init__() に依存を渡す
セッター注入 プロパティやメソッド経由で依存を注入
インタフェース注入 オブジェクトにインジェクションを要求させる

Pythonでは主にコンストラクタ注入が使われる


3. DIを使ったテスト容易な設計

class UserService:
    def __init__(self, repository):
        self.repository = repository

    def get_user(self, id):
        return self.repository.find(id)

✅ テスト時

class MockRepository:
    def find(self, id):
        return {"id": id, "name": "Mock User"}

service = UserService(MockRepository())
assert service.get_user(1)["name"] == "Mock User"

外部依存を切り離すことで、テストをシンプルかつ高速に保てる


4. DIコンテナの導入(例:injector

pip install injector
from injector import Module, Injector, singleton, inject

class Repository:
    def get(self): return "RealData"

class AppModule(Module):
    def configure(self, binder):
        binder.bind(Repository, to=Repository, scope=singleton)

class Service:
    @inject
    def __init__(self, repo: Repository):
        self.repo = repo

injector = Injector([AppModule()])
svc = injector.get(Service)

→ DIコンテナにより 依存の自動解決とスコープ管理 が可能


5. DIを活かす設計のポイント

  • 明示的な依存関係の記述:依存を隠蔽しない
  • インターフェースによる抽象化:具象型に縛られない
  • 呼び出し側が注入責任を持つ:構成の柔軟性を高める

6. 実務的ユースケース

✅ リポジトリの切替(DB vs メモリ)

class MemoryRepo:
    ...

class DBRepo:
    ...

→ 実行環境ごとに UserService(repo=...) で切り替え


✅ APIクライアントのMock化

class APIClient:
    def fetch(): ...

class MockClient:
    def fetch(): return {"mock": True}

svc = SomeService(client=MockClient())  # テストで注入

7. よくある誤用と対策

❌ シングルトン依存を直接呼ぶ

class Service:
    def __init__(self):
        self.config = Config.get_instance()  # 💥 密結合

→ ✅ Service(config) と注入可能にすべき


❌ コンストラクタが複雑すぎる

→ ✅ 依存は集約しすぎず分割して管理する(ファサード/ドメインごと)


結語

DIとは“設計における依存の制御権を呼び出し側に戻す”ことであり、
それは単なる技術的選択ではなく、責務分離・可読性・柔軟性を実現する設計原則である。

  • 密結合を解消し、変化に強い構造を構築
  • テストしやすいコードベースを標準化
  • 明示的で動的な依存制御が拡張性を担保する

Pythonicとは、“動的で柔軟な言語特性の中に、明確な設計の意志を持ち込む”ことであり、
DIはその意志を、構造として体現する最適な手段である。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?