全てをやる「クラス」
さて、昨日は「FizzBuzzを行うプログラム」の抽象化をクラスを使って実現した。
fizzbuzz_task = FizzBuzz()
fizzbuzz_task.run()
これでFizzBuzzの使い勝手は大いに向上したと言えるけれども、こうして愚直に要約したことで柔軟性が失われていることには気づいているだろうか?
変更を加えてみる
初日に立ち返ってみると、「機能拡充に強いプログラム」というのが我々の目指す場所であったと思う。
ではここで機能追加だ。FizzBuzzの結果を標準出力ではなく、ファイルにも書き出せるようにしよう。
fizzbuzz_task = FizzBuzz()
fizzbuzz_task.run(output="file")
まあ、悪くはない。関数の挙動を引数で指定するのは定番だ。
更に変更を加える
では次、FizzBuzzの結果を別のプログラムで利用したいので変数へと保存できるようにしよう。画面などへの出力はなし。
fizzbuzz_task = FizzBuzz()
text = fizzbuzz_task.run(output="none")
これまではFizzBuzzの結果はそのまま出力していたので値を返していなかったが、要望に応じて結果をそのまま返す作りに変更した。
そもそも変更する必要があるのか
……なんだか、fizzbuzz_task.run()が出力も担当しているのは無駄な気がしてきた。値を返せるようにしたのならば……
fizzbuzz_task = FizzBuzz()
fizzbuzz_text = fizzbuzz_task.run()
print(fizzbuzz_text)
こうしておけば、今後FizzBuzzの結果の出力についてどのような変更を加えたくなったとしても、FizzBuzzクラスには変更を加える必要が無くなった。
FizzBuzzは「どのように出力するのか」ということについて責任を持たなくなったからだ。
どこまで要約するかを意識する
クラスというのは何らかの事柄について責任を持っているものである。
冒頭のFizzBuzzは
- FizzBuzzの回数を取得する
- FizzBuzzの結果を生成する
- FizzBuzzの結果を出力する
この3点について責任を持っていた。つまりこれらに関して変更が必要な時はFizzBuzz自体に変更を加えなければならない。
ただ、今回の要件では出力形態について様々な要望があった。出力について要望があるたびに変更が発生していたが、その都度FizzBuzzの実装に踏み込まなければならず、余計な手間とリスクが発生してしまっていた。 これでは「機能拡充に強いプログラム」とは違う気がする。
責任の分け方の定石
要約は当然必要だが、むやみやたらに詰め込むのではない。
「変更が多く見込まれる部分」についてはあらかじめ分けて考えることで、機能拡充に備えておく必要がある。
ちなみに、今回のように「計算(ロジック)」と「入出力(表示)」をあらかじめ分けておくやり方は、Web開発などでよく聞く 「三層アーキテクチャ」 などの設計パターンの基礎となっている。
雑談
今年も色々なものを買ったんですが、ホットクックが素晴らしかったので宣伝。
料理で火を使う工程を代替してくれる調理家電なんですが、火を見なくて良いのは勿論、コンロ周りを掃除しなくて良い事が凄い助かる。
圧力鍋機能は無いんですがその分内鍋、内蓋がシンプルな作りなので使い終わった後のお手入れも楽。
今年買った物の中で一番気に入ってるかもなぁ。
