オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方を読んで、テンプレートメソッドについて学習したので解説します。
テンプレートメソッドとは?
スーパークラス内で基本の構造を定義し、サブクラス固有の貢献を得るためにメッセージを送るテクニック
これだけ読んでピンとくる人はいないと思います。
具体的なサンプルを用いた説明
抽象化された言葉で説明されてもこの手の概念はスッキリ理解しづらいところがあります。
とにかく具体例を見て感覚を掴みましょう。
状況設定
今回の例の状況を設定します。
- 自転車を管理するアプリケーションを作成している
- アプリケーションでは自転車のスペアパーツを一覧表示できる
- タイヤのサイズやチェーンのサイズを持っている
クラス同士の関係はこんな感じになっています。
要件は次のようになっています
- 自転車はチェーン(chain)とタイヤサイズ(tire size)を持つ
- 全ての自転車はチェーンについて同じ初期値を共有する
- サブクラスは、タイヤサイズについて独自の初期値を持つ
- サブクラスの個々のインスタンスは初期値を無視し、インスタンス固有の値を持つことが許される
例
class Bicycle
attr_reader :size, :chain, :tire_size
def initialize(args={})
@size = args[:size]
@chain = args[:chain] || default_chain
@tire_size = args[:tire_size] || default_tire_size
end
def default_chain
'10-speed'
end
end
class RoadBike < Bicycle
# ...
def default_tire_size # <- サブクラスの初期値
'23'
end
end
class MountainBike < Bicycle
# ...
def default_tire_size # <- サブクラスの初期値
'2.1'
end
end
このようにBicycle
はサブクラスに共通のアルゴリズムを提供しています。
サブクラスがアルゴリズムに影響を与えることを許す場所に関してBicycle
はメッセージ(ここではdefault_tire_size
)を送っています。
一方サブクラス側ではそれと同じメソッドを定義することによって、そのアルゴリズムの役割を果たせるようにしています。
このように記述することによって
- 抽象的なベースのクラスには不変的なアルゴリズム
- 具体的なサブクラスには詳細なアルゴリズム
を実現することができます。
不完全なところ
しかし上のコードではまだ不完全な部分があります。
コードが今のままなら不都合は浮き出てきませんが、新たなサブクラスが追加された時に不都合が出てきます。
ここでは例としてサブクラスをRecumbentBike
とします(そういう自転車があるらしい)
上記のアルゴリズムの設計者なら何の問題もなく追加のサブクラスを実装できるかもしれませんが、ここでは設計者以外のプログラマーがサブクラスの追加を行ったとします。
class RecumbentBike < Bicycle
# some code
end
追加を行ったプログラマーが上記の設計を理解しないままdefault_tire_size
の実装を怠ったとします。
上記の設計はサブクラスにdefault_tire_size
があることを前提としているのでもちろんエラーが出ます。
今回のプログラマーが起こすようなエラーを想定して
class Bicycle
attr_reader :size, :chain, :tire_size
def initialize(args={})
@size = args[:size]
@chain = args[:chain] || default_chain
@tire_size = args[:tire_size] || default_tire_size
end
def default_chain
'10-speed'
end
+ def default_tire_size
+ raise NoImplementedError,
+ "This #{self.class} cannot respond to:"
+ end
end
このようにエラー文が出るようにしておくことで、もし間違いが起きてもすぐに原因を発見できるようになります。