Python基礎 オブジェクト指向SOLID(依存性逆転の原則)
| 登録日 | :2025/09/18 04:29 |
|---|---|
| カテゴリ | :Python基礎 |
D: Dependency inversioin(依存性逆転の原則)
-
上位のモジュールは下位のモジュールに依存してはならない。どちらもモジュールの「抽象」に依存すべき。
(モジュール:ソフトウェアのひとまとまりの機能) -
「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。
原則に違反すると
-
下位モジュールの変更が上位モジュールに影響を与える
-
下位モジュールがないと上位モジュールが開発できない
-
モジュールの拡張性・再利用性が低い
-
単体テストが困難
解決策
上位モジュールと下位モジュールをインターフェースに依存させる
補足(Dependency Injection)
クラス間の依存関係をソースコードから排除するために、コンストラクタやセッターメソッドを通じて外部からオブジェクトを渡せるようにするパターン
class UserService:
def __init__(self):
self.__user_repository = UserRepository()
上記のように、クラスの中で、別のクラスをインスタンス化するのではなく、インスタンス化したものを引数に受け取るように修正する
class UserService:
def __init__(self, user_repository: IUserRepository):
self.__user_repository = user_repository
IUserRepositoryという抽象クラスの型を指定することで、UserRepositoryの変更の影響を受けない。
UserRepositoryが開発されていなくても、開発を進めることができる。
本番環境とテスト用のコードを分けることも容易。
補足(DIコンテナとは)
pythonでは別モジュールが必要
自動でDIを行なってインスタンスを構築してくれる仕組み
DIコンテナにクラスと生成方法を登録しておき、DIコンテナ経由でインスタンス化を行うことで、事前に登録しておいた状態でインスタンスを取得可能
サンプルコード
from abc import ABCMeta, abstractmethod
class User(object):
pass
class IUserService(metaclass=ABCMeta):
@abstractmethod
def create(self, _user: User) -> User:
pass
@abstractmethod
def find_by_id(self, _id: str) -> User:
pass
class UserController(object):
def __init__(self, _user_service: IUserService):
self.__user_service = _user_service
def create(self, _user: User) -> User:
return self.__user_service.create(_user)
def find_by_id(self, _id: str) -> User:
return self.__user_service.find_by_id(_id)
class IUserRepository(metaclass=ABCMeta):
@abstractmethod
def create(self, _user: User) -> User:
pass
@abstractmethod
def find_by_id(self, _id: str) -> User:
pass
class UserService(IUserService):
def __init__(self, _user_repository: IUserRepository):
self.__user_repository = _user_repository
def create(self, _user: User) -> User:
return self.__user_repository.create(_user)
def find_by_id(self, _id: str) -> User:
return self.__user_repository.find_by_id(_id)
class UserRDBRepository(IUserRepository):
def create(self, _user: User) -> User:
print("Register RDB: User")
return _user
def find_by_id(self, _id: str) -> User:
print(f"ID: {_id} ")
return User()
if __name__ == '__main__':
user_rdb_repository = UserRDBRepository()
#DIコンテナ
user_service = UserService(user_rdb_repository)
user_controller = UserController(user_service)
user_controller.find_by_id('123')