VOOZH about

URL: https://qiita.com/kidach1/items/7c2a80bfc8a87a05051f

⇱ Rubyによるデザインパターン【Template Method】-テンプレは準備した、あとはお好きに- #プログラミング - Qiita


👁 Image
51

Go to list of users who liked

44

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

More than 5 years have passed since last update.

@kidach1(404 Not Found)in👁 Image
株式会社アカツキ

Rubyによるデザインパターン【Template Method】-テンプレは準備した、あとはお好きに-

51
Last updated at Posted at 2014-06-15

概要

Rubyによるデザインパターン第3章。
Template Method Pattern。

Rubyによるデザインパターン5原則に則って理解する。

どんなパターンか

ソフトウェアには変化がつきものである。
その変化の度に、広範囲な修正が入っているようではとても対応できない。

そこで、
抽象的な処理と具体的な処理を分離することで、
変化に強い構造を実現したい。

そのための手段の一つがTemplate Method。

骨子は以下2種のクラスの利用。

  • 骨格となるメソッドを持った抽象基底クラス(テンプレートクラス)
  • 処理の詳細を詰める具象クラス(サブクラス)

ひどいコード

あるレポートを様々なフォーマット(HTMLやPlainText)で出力するReportクラス

class Report
 def initialize
 @title = '月次報告'
 @text = ['最高!', '順調', '普通']
 end

 def output_report(format)
 if format == :plain
 puts("*** #{@title} ***")
 elsif format == :html
 puts('<html>')
 puts(' <head>')
 puts(" <title>#{@title}</title>")
 puts(' </head>')
 puts(' <body>')
 else
 raise "Unknown format: #{format}"
 end

 @text.each do |line|
 if format == :plain
 puts(line)
 else
 puts(" <p>#{line}</p>")
 end
 end
 if format == :html
 puts(' </body>')
 puts('</html>')
 end
 end
end

フォーマットがHTMLなのかPlainTextなのかによって、
都度if分で処理を分けている。
フォーマットがさらに増えたらどうなるのか・・

ということで、解決策を考える。

パターンの適用によるリファクタリング

変わるものと変わらないものを分離する

  • 基本的な処理の流れ・テンプレート(変わらないもの)をフォーマット(変わるもの)に依存させるべきではない。
  • 前者は抽象的な基底クラスに、後者は具象サブクラスに定義する。

つまり

「基本的な処理の流れ」である、

  1. 特定フォーマットに必要なヘッダ情報を出力する。
  • タイトルを出力する。
  • レポート本体(body)を出力する。
  • フォーマットに要求される残りの要素を出力する。

これらを「テンプレ」として抽象基底クラスに定義し、残りはサブクラスへ切り出す。

実装

抽象基底クラス

class Report
 def initialize
 @title = '月次報告'
 @text = ['最高!', '順調', '普通']
 end

 def output_report
 output_start
 output_title
 output_body_start
 @text.each do |line|
 output_line(line)
 end
 output_body_end
 output_end
 end

 def output_start
 end

 def output_title
 output_line(@title)
 end

 def output_body_start
 end

 def output_line(line)
 raise 'Called abstract method: output_line'
 end

 def output_body_end
 end

 def output_end
 end
end

output_start, output_title, output_body_start, output_body_end, output_endのように
***具象クラスによってオーバーライドできる(不要ならしなくてもよい)***メソッドのことを
フックメソッドと呼ぶ。

一方でoutput_lineは、直接呼び出すと例外を発生させる。つまり、
具象クラスによるオーバーライドを強要している。

→output_lineは、「各フォーマットごとに処理が異なる=より変化しやすい処理」
という想定があるため。

具象サブクラス1(フォーマット:HTML)

class HTMLReport < Report
 def output_start
 puts('<html>')
 end

 def output_title
 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

具象サブクラス2(フォーマット:PlainText)

class PlainTextReport < Report
 def output_title
 puts("*** #{@title} ***")
 end

 def output_line(line)
 puts(line)
 end
end

クラスの利用

[15] pry(main)> report = HTMLReport.new
[16] pry(main)> report.output_report
<html>
 <head>
 <title>月次報告</title>
 </head>
 <body>
 <p>最高</p>
 <p>順調</p>
 <p>普通</p>
 </body>
</html>

[19] pry(main)> report = PlainTextReport.new
[20] pry(main)> report.output_report
*** 月次報告 ***
最高!
順調
普通

まとめ

TemplateMethodパターンにより、

変わるもの(具象サブクラス)と変わらないもの(テンプレート)を分離して、変化に強い構造へ。

以下へ続く

【Strategy】-取り替え可能パーツ群を戦略的に利用せよ-
http://qiita.com/kidachi_/items/02f8f6df955ba1d0e93b

51

Go to list of users who liked

44
0

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51

Go to list of users who liked

44