「〇〇りっちなう」 うーん、どのぐらいの人に伝わるんだろう。わからなかったら、なんかそういう Twitter ボットがあるんだなと思っておいてください。
複数の似たプログラムを書く必要があるとき、コピーで作ってしまうと、変更差分にならなかった部分がまったく同じコードとして残ってきます。まるごと明日使い捨てるならコピーでもいいのですが、ある程度運用するものをコピーでやるのはまずいですね。
共通部分にもし修正が必要になったら、コピーの数だけ同じ修正を当てないといけません。だいたいそういうときって、どこか変更し忘れたり、そっくりそのまま同じ変更になってなかったりするんでよ。後で見たら、同じはずなのに互いに異なっているコードがあって、どれが正しいのかわからなくなったりします。
共通部分は同じソースコードを共有しましょう。
ってとき、よしならば共通関数ライブラリだ、ってやってしまうと、やっぱり同じ構造を維持できないじゃん、となるかもしれません。ライブラリをどのように呼び出すかは使用者の自由なので、ちょっとづつ違う変種が生まれる可能性は捨てきれないのです。
というわけでめもりーちゃんが提案してくれたのが、Template Method パターンです。
まず、全体の作りはできているけど、内部で使う処理の部分部分が歯抜けメソッド (指定できるなら、可視性は protected スコープ) になったような抽象クラスを設けます。それを継承する複数の子クラスの側で、それぞれの派生の都合に合わせてこの歯抜けを定義します。
そけっとさんの仕事の場合、Twitter API のお作法はどれも同じで、「送信対象の収集アルゴリズム」と「送信内容の決定」が、前回と今回で違う部分になります。
こうしておくと、全体的な保守のとき、抽象クラス側をいちどだけ修正すれば、どちらのボットも同じように直ります。将来また同じようなボットを作らないといけないときは、派生を追加すればすぐにできます。
Template Method パターンは、いわば、クラス数最少のフレームワークです。
これ、別解として、「どんな文字列に反応するか」と「どんな文字列を送るか」をパラメーターにして、ひとつのクラスで済ませるやり方も考えられます。
しかし、固定の文字列って、なんか一般化しきれてなさそうで不安になりません? もし反応するキーワードが複数あったら... もし送信内容に動的な変化を求められたら...
じゃあ文字列の代わりに、関数か Strategy を与えればどうか。はい、それはたしかに大正解の王道です。継承よりコンポジションを好む方が優れた設計でしたよね。ただ、トレードオフを考える必要があります。
関数の入出力型を厳密に表すことができる言語でないと、もしかしたら、実行時に壊れる関数が渡されているかもしれません。そもそも言語仕様に関数ポインタがない可能性だってあります。
なので言語によっては、「Strategy 抽象の定義」が余分に必要になるかもしれません。むしろ、「こんな入出力の関数ね」とアドホックにやると、分散したコードの意味が伝わりにくいので、あえて名前をつけて定義したほうが良いかも...
また、動的に組み合わせるということは、言語に関係なく、正しい組み合わせの関数を持たせたインスタンスの生成を行う責務が外部に発生します。よーし知識を Factory に持たせて...
うーん、だんだんアーキテクチャ宇宙飛行士感が出てきたぞ。
めもりーちゃんはすぐに、そこまでの柔軟性があったところで、どうせ後で生きてこないだろうと考えました。ささいなことだけど、問題に対して大げさすぎる方法はなんかやだ、って嗅覚が本能的にはたらくのです。(←「なんか」が当たるからやはりちょうぜつ)
Template Method なら、どの派生を new
するかだけで使い分けられてお手軽、どんな振る舞いかのセットはハードコードなので、生成時に挙動を与える必要なし。こんなオマケ機能みたいな独立した部分で、ソースファイルを増やすのはやめとこう、って。
Template Method はフレームワークです。また同時に、よりハードコードに近いほうの妥協です。というかフレームワークはそもそも、柔軟性を犠牲にした、生産性や保守性の獲得ですよね。コンポジションによる柔軟性より、継承による硬直化をあえて選ぶのがこのパターンです。最良の戦略は場合による、ということに注意してください。