Template Method
アルゴリズムに多様性を持たせたい場合に役に立つ。
基底クラスには、不変の部分を記述し、変わる部分はサブラクスに定義するメソッドにカプセル化する。
基底クラスは、メソッドを未定義にしておくことができる。ただしその場合は、サブクラスでそのメソッドを提供しなくてはならない。また、未定義にするかわりに、基底クラスで標準実装し(フックメソッド)、必要な場合のみサブクラスでオーバーライドさせることもできる。
【重要】
変化するものを変わらないものから分離する
変わらないもの=テンプレートメソッドに記述された基本的なアルゴリズム
変化するもの=サブクラスで提供される詳細な処理
【語句】
○基底クラス
あるクラスの仕様を継承して、新しいクラスを作る際に、継承元となるクラスのこと。親クラス。
○抽象クラス(Abstract Class)
クラス構造の雛形を定義する目的だけのために作られたクラスのこと。具体的な処理は、これを継承したクラス(サブクラス)に記述させる。抽象クラスの存在意義は、複数のクラスにたいして共通性を持たせることであり、クラス設計においてとても重要な役割を担う。
テンプレートメソッドパターンで言えば、変わらない処理をまとめるためのクラス。
サブクラスが共通して持つメソッドを抽象的に持つクラスのことと言える。
○具象クラス(Concrete Class)
抽象クラスではない、通常のクラス。
○メソッドのオーバーライド
サブクラス側でスーパクラスと同じ名前を持つメソッドを定義すること。サブクラスで定義したメソッドが優先される。オーバーライドされた場合であっても、スーパークラス側のメソッドを呼び出すことは可能。
○フックメソッド
Template Methodの具象クラスによってオーバーライドできる非抽象メソッドのことをフックメソッドと呼ぶ。
フックメソッドを使うと、具象クラスは、①基底実装をオーバーライドすることで別の処理を実行させるか、②標準実装をそのまま使うかを選択することができる。
○ダックタイピング
「いま私であるものこそが私なのだ」という型に対するアプローチ。
If it walks like a duck and quacks like a duck, it must be a duck.
どのクラスからのインスタンスなのかなど気にしない、重要なのは「そのインスタンスはなにができるのか」というアプローチ。
○サブクラス = 子クラス = 派生クラス
○親クラス = 基底クラス
今まで読んだ書籍を思い出す限り、Rubyではサブクラスと使うのが多いようだ。
【サンプルコード】
# -*- encoding: utf-8 -*- # 抽象基底クラス class Report def initialize @title = '月次報告' @text = ['順調', '最高の調子'] end # このメソッドはオーバーライドされない! # このメソッドがサブクラスがオーバーライドするメソッドに # よって行われる具体的な処理をまとめて出力している。 # つまり、これがテンプレートメソッドにあたる。 def output_report output_start output_head output_body_start output_body output_body_end output_end end # 実際に処理をおこなっていないメソッドをなぜわざわざ記述し、オーバーライドさせる必要があるのだろうか? # → 抽象基底クラスであるこのReportクラス内に、形だけでもこれらのメソッドがないと、上のテンプレートメソッド内で # No Method Errorが起きてしまうためではないだろうか。あとは、インターフェイスを近づけるためとか? def output_body @text.each do |line| output_line(line) end end # このメソッドのように、なにも記述されていないメソッドはサブクラスの利便性向上の為にこうなっている # PlainTextReportクラスでは、ここの処理が全く必要ないため。 # このようにしないと、PlainTextReportクラスになにもしないメソッドを書かなくてはならなくなる # こういったメソッドを「フックメソッド」と呼ぶ。 def output_start end def output_head output_line(@title) end # フックメソッド def output_body_start end # これもフックメソッド def output_line(line) raise 'Called abstarct method: output_line' end # フックメソッド def output_body_end end # フックメソッド def output_end end end # 具象サブクラス class HTMLReport < Report def output_start puts '<html>' end def output_head puts '<head>' puts "<title>#{@title}</title>" puts '<head>' end def output_body_start puts '<body>' end def output_line(line) puts "<p>#{line}</p>" end def output_body_end puts '</body>' end def output_end puts '</html>' end end # 具象サブクラス class PlainTextReport < Report def output_head puts "**** #{@title} ****" end def output_line(line) puts line end end report = HTMLReport.new report.output_report report = PlainTextReport.new report.output_report
【出力結果】
<html> <head> <title>月次報告</title> <head> <body> <p>順調</p> <p>最高の調子</p> </body> </html> **** 月次報告 **** 順調 最高の調子