カウンタクラス(機能合成)
今回作るのは、この章でいちばん小さいクラスです。持つ状態は数値 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") を呼びます。
親子にするかどうかの目安は、子が親の一種だと言い切れるかどうかです。気温計はセンサーの一種なので自然に繋がります。メソッドがたまたま似ているだけなら、無理に親を作らないほうが後で困りません。
やってみよう
counterOps(ops) を書いてください。数値を 1 つ属性に持つクラスを定義し、増やす、減らす、ゼロに戻すの 3 つのメソッドを用意します。ops は "inc" "dec" "reset" のいずれかが並んだ配列です。1 つずつ取り出して対応するメソッドを呼び、最後に属性の値を整数で返します。空配列なら、初期値のままの値が答えです。
self.count += 1のつもりでcount += 1と書くと、その場限りのローカル変数が 1 になって終わります。エラーは出ず、属性は増えないままです。属性を書き換えるつもりのときは、必ずselfかthisを頭に付けてください。
要件
- 関数
counterOps(ops)を実装し、最終的なカウンタ値を整数で返す - 内部に
Counterクラスを定義し、value属性とinc/dec/resetメソッドを持たせる - ops を 1 つずつ取り出し、対応するメソッドを呼ぶ
入出力例
counterOps(["inc","inc","dec","inc"]) → 2
counterOps(["inc","inc","reset","inc"]) → 1
counterOps(["inc","inc","inc","reset"]) → 0
counterOps(["dec","dec","dec"]) → -3
counterOps([]) → 0
counterOps(["inc","inc","inc","inc","inc"]) → 5