高水準なモジュールは、低水準なモジュールに依存してはいけません。両者は抽象化に依存すべきとする原則です。
依存性逆転の原則を守ることで、低水準のモジュールを継承したクラスを利用した機能拡張が容易になります。
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)