コードの読みやすさと保守性を高めるために、モジュール化は非常に重要です。
本記事では、依存性の注入(Dependency Injection, DI)を使ってコードをモジュール化する方法について解説します。
サンプルコードを交えながら、DIの利点と実践的な適用方法を見ていきましょう。
ハードコーディングされた依存関係の問題点
以下のコードは、ユーザー認証を行うAuthenticatorクラスの一部です。
class Authenticator:
def __init__(self):
self.user_repository = MySQLUserRepository()
def authenticate(self, username, password):
user = self.user_repository.find_by_username(username)
if user and user.password == password:
return True
return False
このAuthenticatorクラスは、ユーザー情報を取得するためにMySQLUserRepositoryクラスに依存しています。
しかし、この依存関係はAuthenticatorクラスのコンストラクタ内でハードコーディングされています。
つまり、Authenticatorクラスは常にMySQLデータベースからユーザー情報を取得することになります。
この実装には以下のような問題があります。
- 他のデータベースやデータ取得方法に切り替えることができない
- テストの際に、実際のMySQLデータベースに依存してしまう
- MySQLUserRepositoryクラスの変更がAuthenticatorクラスに影響を与える
これらの問題は、クラス間の密結合によって引き起こされています。
DIを使った依存関係の注入
上記の問題を解決するために、DIを使ってUserRepositoryをAuthenticatorクラスに注入してみましょう。
class Authenticator:
def __init__(self, user_repository):
self.user_repository = user_repository
def authenticate(self, username, password):
user = self.user_repository.find_by_username(username)
if user and user.password == password:
return True
return False
class UserRepository:
def find_by_username(self, username):
raise NotImplementedError()
class MySQLUserRepository(UserRepository):
def find_by_username(self, username):
# MySQLデータベースからユーザー情報を取得する処理
pass
class MongoDBUserRepository(UserRepository):
def find_by_username(self, username):
# MongoDBデータベースからユーザー情報を取得する処理
pass
この変更点は以下の通りです。
- UserRepositoryインターフェースを定義し、find_by_usernameメソッドを持つようにしました。
- MySQLUserRepositoryクラスとMongoDBUserRepositoryクラスは、UserRepositoryインターフェースを実装しています。
- Authenticatorクラスのコンストラクタは、UserRepositoryインスタンスを受け取るようになりました。
これにより、以下のようなメリットが得られます。
- データベースやデータ取得方法を柔軟に切り替えられる
- テストの際に、モックオブジェクトを注入できる
- UserRepositoryの実装変更がAuthenticatorクラスに影響を与えない
Authenticatorクラスの使用例を以下に示します。
# MySQLを使った認証
mysql_repo = MySQLUserRepository()
authenticator = Authenticator(mysql_repo)
result = authenticator.authenticate("john_doe", "password123")
# MongoDBを使った認証
mongodb_repo = MongoDBUserRepository()
authenticator = Authenticator(mongodb_repo)
result = authenticator.authenticate("jane_smith", "qwerty456")
このように、DIを使うことでAuthenticatorクラスとUserRepositoryの実装を分離し、モジュール化を促進できます
ファクトリ関数の活用
DIを使ったコードは、モジュール化と柔軟性が向上する一方で、クラスの構築が少し複雑になるというデメリットがあります。
この問題を軽減するために、ファクトリ関数を活用することができます。
def create_mysql_authenticator():
mysql_repo = MySQLUserRepository()
return Authenticator(mysql_repo)
def create_mongodb_authenticator():
mongodb_repo = MongoDBUserRepository()
return Authenticator(mongodb_repo)
これらのファクトリ関数を使うことで、必要な依存関係を簡単に構築できます。
authenticator = create_mysql_authenticator()
result = authenticator.authenticate("john_doe", "password123")
ファクトリ関数を使えば、依存関係の構築をカプセル化し、コードの可読性を高めることができるでしょう。
まとめ
依存性の注入(DI)は、コードのモジュール化と柔軟性を高めるための強力な手法です。
ハードコーディングされた依存関係を避け、インターフェースを使って依存関係を注入することで、コードの結合度を下げることができます。
また、ファクトリ関数を活用することで、依存関係の構築を簡素化し、コードの可読性を高めることができます。
DIを効果的に使うことで、より保守性の高いコードを書くことができるでしょう。

