nonoのポートフォリオサイト

SOLIDの原則

SOLIDの原則5 依存性逆転の原則

高水準なモジュールは、低水準なモジュールに依存してはいけません。両者は抽象化に依存すべきとする原則です。

依存性逆転の原則を守ることで、低水準のモジュールを継承したクラスを利用した機能拡張が容易になります。


原則

  1. 高水準なモジュールは、低水準のモジュールに依存してはいけない。  両者は抽象化に依存すべき
  2. 抽象化は詳細に依存すべきではなく、詳細は抽象化に依存すべき。

class A(metaclass=ABCMeta):
pass

class B(A):
pass

このようにクラスBがクラスAを継承している場合、
例えば新たなクラスCは、クラスBではなく、クラスAを継承するようにする。
class C(A):
pass


サンプルコード


    # dependency_inversion.py
    # 修正コード
    
    from abc import ABCMeta, abstractmethod, abstractproperty
    
    
    class IBook(metaclass=ABCMeta):
        """
        インターフェースとなる抽象クラスを定義
        頭文字に'I'をつけて表すことが多い
        """
        @abstractproperty
        def content(self):
            pass
    
    
    class Book(IBook):
        """IBookを継承した、詳細クラスの作成"""
        def __init__(self, content):
            self._content = content
    
        @property
        def content(self):
            return self._content
    
    
    class EBook(IBook):
        """IBook を継承して、別の詳細クラスを定義する
          これにより、EBookも、IBookを前提として作成されている様々なクラスが
          そのまま活用できることになる。
        """
        def __init__(self, content):
            self._content = content
    
        @property
        def content(self):
            return 'E-' + self._content
    
    
    class IFormatter(metaclass=ABCMeta):
        """ 抽象クラスIBookを引数とする、抽象クラスのIFormatterを定義する"""
        @abstractmethod
        def format(self, i_book: IBook):
            pass
    
    
    class HtmlFormatter(IFormatter):
        """ IFormatterを継承した詳細クラスの作成"""
        def format(self, i_book: IBook):
            return '<h1>' + i_book.content + '</h1>'
    
    
    class XMLFormatter(IFormatter):
        """このようにすることで、
           新しくXMLのフォーマッターを作成する場合に継承できる
        """
        def format(self, i_book: IBook):
            return '<xml/>' + i_book.content + '</xml>'
    
    
    class Printer(object):
        """ 引数とするクラスは、常に抽象クラスとする(詳細クラスにしない)"""
        def __init__(self, i_formatter: IFormatter):
            self.i_formatter = i_formatter
    
        def print(self, i_book: IBook):
            formatted_book = self.i_formatter.format(i_book)
            print(formatted_book)

作成したクラスをメインロジックの中で以下のように使います。


    if __name__ == '__main__':
        book = Book('My Book')
        html_formatter = HtmlFormatter()
        html_printer = Printer(html_formatter)
        html_printer.print(book)
    
        xml_formatter = XMLFormatter()
        xml_printer = Printer(xml_formatter)
        xml_printer.print(book)
    
        ebook = EBook('My EBook')
        # プリンターを操作せずに活用ができる!
        html_printer.print(ebook)