KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

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')