クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

継承より委譲を選ぶ

このレッスンのあとで: 継承ではなくコンポジションを選ぶ理由を言い、部品を取り替える設計を書くことができる。

60分のレッスンコマンド5件毎日使う Linux コマンドのラボ:エンジニアが暗記している30のコマンド

継承より委譲を選ぶ

コンポジションとは、クラスが別のオブジェクトを部品として持ち、仕事の一部をその部品に任せる設計です。継承の代わりになる、もう1つの組み立て方です。

部品を取り替えるだけで動きが変わるので、組み合わせが増えても、クラスの数が爆発しません。

このレッスンのゴール

メモ

継承ではなくコンポジションを選ぶ理由を言い、部品を取り替える設計を書くことができる。

キーワード

ことばいみれい
コンポジションオブジェクトを部品として持って組み立てる設計
委譲自分で計算せず、持っている部品に仕事を任せることself.tax.add(x)
has-a 関係「AはBを持っている」と言える関係
is-a 関係「AはBの一種だ」と言える関係。継承の形
部品の取り替え持っている部品を、同じ呼び方ができる別の部品に替えること

学生が入力するセルは、このプレビューでは空白です。

継承だけだと、クラスが組み合わせの数だけ増える

レシートに、税の種類と、割引の種類があるとします。継承で作ると、「10%の税で学生割引」のように、組み合わせごとにクラスが要ります。

税が3種類、割引が3種類なら、9つです。1つ増えるたびに、さらに3つ増えます。

例: 継承と部品の、クラスの数

税の種類割引の種類継承で作るクラス部品で作るクラス
3396(税3と割引3)
43127(税4と割引3)
45209(税4と割引5)

学生が入力するセルは、このプレビューでは空白です。

クラスが部品を持てば、仕事を任せられる

レシートは、税の計算そのものを自分で書きません。税の部品を1つ持ち、計算はその部品に頼みます。これが委譲です。

「レシートは税を持っている」と言えます。これが has-a 関係です。「レシートは税の一種」とは言えないので、継承は合いません。

税の部品を持つレシート

コード: python
class TenPercent:
    def add(self, yen):
        return yen * 110 // 100


class EightPercent:
    def add(self, yen):
        return yen * 108 // 100


class Receipt:
    def __init__(self, tax):
        # 税の計算を、部品として1つ持ちます。
        self.tax = tax

    def total(self, prices):
        subtotal = 0
        for price in prices:
            subtotal = subtotal + price
        # 税の計算は、部品に任せます。
        return self.tax.add(subtotal)


receipt = Receipt(TenPercent())
print(receipt.total([100, 250]))
# 次の行で、部品を取り替えます。
receipt.tax = EightPercent()
print(receipt.total([100, 250]))

部品は動いている最中にも取り替えられる

継承は、クラスを書いた時点で決まります。作ったオブジェクトを、後から別の種類に変えることはできません。

部品は属性なので、プログラムの動いている最中に取り替えられます。軽減税率の日だけ、税の部品を替える、といった使い方ができます。

ラボで部品を取り替える

ラボの端末で、税の部品とレシートのファイルを作り、1行ずつ動かします。5つの Run を、上から順に押してください。

同じレシートが、部品しだいで違う合計を返すのを確かめます。

作業用のフォルダを作る

コマンド
mkdir -p ~/oop/composition

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

そのフォルダへ移る

コマンド
cd ~/oop/composition

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

部品とレシートを書く

コマンド
printf 'class TenPercent:\n    def add(self, yen):\n        return yen * 110 // 100\n\n\nclass EightPercent:\n    def add(self, yen):\n        return yen * 108 // 100\n\n\nclass Receipt:\n    def __init__(self, tax):\n        self.tax = tax\n\n    def total(self, prices):\n        subtotal = 0\n        for price in prices:\n            subtotal = subtotal + price\n        return self.tax.add(subtotal)\n' > parts.py

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

10%の税で合計する

コマンド
python3 -c "from parts import *; print(Receipt(TenPercent()).total([100, 250]))"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

部品を取り替えて合計する

コマンド
python3 -c "from parts import *; r = Receipt(TenPercent()); r.tax = EightPercent(); print(r.total([100, 250]))"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

同じ合計の関数が、違う答えを返す

1つ目の出力は 385、2つ目は 378 です。値段の合計は、どちらも350円です。

350円に10%の税を足すと385円、8%なら378円です。Receipt は書き換えていません。部品だけを替えました。

よくある間違い

注意

「動きを変えるには、サブクラスを作るしかない」と考える人が多くいます。変えたい所が1つの部品に切り出せるなら、部品を取り替えるほうが簡単です。継承は、本当に is-a と言えるときに選びましょう。

このラボとソースについて: 毎日使う Linux コマンドのラボ:エンジニアが暗記している30のコマンド

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。