カウンタクラス(機能合成)
今回作るのは、この章でいちばん小さいクラスです。持つ状態は数値 1 つ、操作は増やす、減らす、ゼロに戻すの 3 つだけ。小さいからこそ、次に必ず来る困りごとが見えます。似たクラスをもう 1 つ欲しくなったときです。
同じメソッドを 2 か所に書き写した
気温を記録するクラスと、湿度を記録するクラスを別々に書いたとします。
Python
class Thermometer:
def __init__(self):
self.readings = []
def record(self, v):
self.readings.append(v)
def latest(self):
return self.readings[-1]
class Hygrometer:
def __init__(self):
self.readings = []
def record(self, v):
self.readings.append(v)
def latest(self):
return self.readings[-1]違うのはクラス名だけです。この状態で「記録は直近 100 件までにする」と決まったら、2 か所を同じように直すことになります。片方だけ直して忘れる、が起きます。
共通部分を親に上げて、違うところだけ子に書く
Python
class Sensor:
def __init__(self, unit):
self.unit = unit
self.readings = []
def record(self, v):
self.readings.append(v)
def latest(self):
return self.readings[-1]
class Thermometer(Sensor):
def __init__(self):
super().__init__("C")
def label(self):
return "気温"class Thermometer(Sensor) と書くと、Sensor が持つメソッドはそのまま使えるようになります。super().__init__("C") は親の初期化を呼び出す書き方です。Thermometer().record(21) が実行しているのは、Sensor 側に 1 つだけ書かれた record です。仕様が変わっても、親を 1 か所直せば両方に届きます。JavaScript も形は同じで、class Thermometer extends Sensor と書き、constructor の先頭で super("C") を呼びます。
親子にするかどうかの目安は、子が親の一種だと言い切れるかどうかです。気温計はセンサーの一種なので自然に繋がります。メソッドがたまたま似ているだけなら、無理に親を作らないほうが後で困りません。
やってみよう
ひな形の TODO を完成させてください。増加・減少・リセットによる属性の更新を自分で書きます。入力を受け取る枠と説明つきの準備部分はそのまま使えます。
self.value += 1のつもりでvalue += 1と書くと、属性の更新にはなりません。ローカル変数 value を初期化していなければエラーになります。属性を書き換えるつもりのときは、必ずselfかthisを頭に付けてください。
演習で使う書き方
本文のセンサーは記録を属性に持ちます。演習のカウンターは現在値を属性に持ち、メソッドで変えます。例えば inc、inc、reset、dec の順なら 0 → 1 → 2 → 0 → -1。入力からメソッドを選ぶ部分と最初の 0 は用意し、三つの操作本体を自分で書きます。
要件
- 増加・減少・リセットによる属性の更新を自分で実装する
- 関数名と引数、入力と戻り値の形を保つ。説明つきの準備部分を使い、TODO の仮の処理を置き換える