注意)このTicketはあくまで自分用のメモ。厳密な定義などは参考サイトを参照のこと
継承とコンポジションについて学ぶ
参考サイト1の動画で紹介されているTypeScriptのコードを自分なりにpythonに書き直して学習する。参考サイト1のコードはpythonにないインターフェースを利用した説明のため、今回は参考サイト3のコードの方が理解しやすい。しかし、勉強のため参考サイト1のコードを書き直したもので下記に記述する。
継承とは
pythonでの書き方
from abc import ABCMeta, abstractmethod
#インターフェース
class Arranger(metaclass=ABCMeta):
@abstractmethod
def arrange(self):
pass
# 継承
class AbstractArranger(Arranger):
def arrange(self,json):
return self.delimiter().join(map(self.format,json))
@abstractmethod
def format(self,book):
pass
def delimiter(self):
return "\n"
class SimpleJsonArrangerExt(AbstractArranger):
def format(self,book):
return book['title'] + "(" + book['author'] + ")"
#--- 動作確認 ------------------------------------
if __name__=='__main__':
BOOKS = [{ 'title':'坊ちゃん', 'author': '夏目漱石' },\
{ 'title': '吾輩は猫である', 'author': '夏目漱石' },\
{ 'title': '友情', 'author': '武者小路実篤' },]
print('### 継承版 ###')
arranger = SimpleJsonArrangerExt()
print(arranger.arrange(BOOKS))
実行結果
### 継承版 ###
坊ちゃん(夏目漱石)
吾輩は猫である(夏目漱石)
友情(武者小路実篤)
コンポジションとは
pythonでの書き方
from abc import ABCMeta, abstractmethod
#インターフェース
class Arranger(metaclass=ABCMeta):
@abstractmethod
def arrange(self):
pass
#インターフェース
class BookFormatter(metaclass=ABCMeta):
@abstractmethod
def format(self):
pass
# コンポジション
class SimpleBookFormatter(BookFormatter):
def format(self,book):
return book['title'] + "(" + book['author'] + ")"
class JsonArranger(Arranger):
def __init__(self,formatter):
self.formatter = formatter
def arrange(self,json):
return "\n".join(map(self.formatter.format,json))
#--- 動作確認 ------------------------------------
if __name__=='__main__':
BOOKS = [{ 'title':'坊ちゃん', 'author': '夏目漱石' },\
{ 'title': '吾輩は猫である', 'author': '夏目漱石' },\
{ 'title': '友情', 'author': '武者小路実篤' },]
print('### コンポジション版 ###')
arranger = JsonArranger(SimpleBookFormatter())
print(arranger.arrange(BOOKS))
実行結果
### コンポジション版 ###
坊ちゃん(夏目漱石)
吾輩は猫である(夏目漱石)
友情(武者小路実篤)
SimpleBookFormatterのインスタンスを渡して、JsonArrangerの中で利用しているのがポイント
JsonArrangerはSimpleBookFormatterを継承していないがインスタンスを渡すことでSimpleBookFormatterのメソッドを利用している
要はコンポジションは、継承を使わず別クラスのメソッドを利用する方法
継承とコンポジション どういったときに使う?
参考サイト2を参照。簡単にまとめると下記の通り。
「AはBである」is-a関係:継承 例.猫は動物である
「AはBを含んでいる」has-a関係:コンポジション 例.自転車はサドルを含んでいる
参考サイト1では明確なis-a関係であれば継承。曖昧な場合(継承とコンポジション両方で書ける場合)は下記利点を考えて選ぶ。悩んだときは柔軟性を重視してコンポジションで実装すると紹介していた。
[クラスの数]
★継承 < コンポジション
[構造の把握しやすさ]
コンポジション < ★継承
[クラスを実装する上での影響範囲の見えやすさ]
継承 < ★コンポジション
[柔軟性]
継承 < ★コンポジション
私の学習したイメージとしては、コンポジションは「部品」のイメージ。クラスを「部品」に分解して利用する。「部品」だから交換などがしやすい。「部品」に分解するからコード量は継承に比べると増える傾向にある。継承は親クラスを理解してコードを記述する必要があるが、「部品」はそれそのもので存在できるので、依存度が低い。
コンポジションを多用するとコードが大変になるときがあるが、ミックスインを併用すると綺麗にかけるときがある(参考サイト3を参照)。