概要
GoFのデザインパターンのVisitorパターンについて。
データ構造と処理の分離を行うことによって
OPC(The Open-Closed Principle)を実現する。
拡張については開かれていて、修正については閉じられている。
VisitorとElementが協調して処理を行う形式を
ダブルディスパッチという。
A,Bがセットで処理を決定する。
登場人物
Visitor = 抽象ビジター。訪問者
ConcreteVisitor = 具象ビジター。訪問者
Element = 受入要素のインターフェース
ConcreteElement = 具象要素
ConcreteStructure = 集合体
実装サンプル
サンプル概要
Compositパターンで使用したWebサイトのリンク構成を実装したクラスを流用。
Webサイト全体はWebPageクラス。リンクを保持するページとリンクを保持しないページがあります。
リンクを保持するページはLinkPageクラス。
リンクを保持しない終端サイトはTerminatePage。
各WebPageを訪問するのがVisitor役のFormatterクラスを実装したCsvFormatter,HatenaFormatterとします。
CsvFormatterはサイトを
サイト名,URL
で表示します。
HatenaFormatterはサイトを
[URL;title=サイト名]
で表示します。
登場人物
Element = Accepter : インターフェース
Component = WebPage : コンポーネント。ウェブページ全般
ConcreteElement = LinkPage : コンポジット。リンクページ。ConcreteStructureも兼ねる
ConcreteElement = TerminatePage : 葉。再帰しない要素。終端ページ
Visitor = Formatter : 整形
ConcreteVisitor = CsvFormatter : CSV整形
ConcreteVisitor = HatenaFormatter : CSV整形
サンプルコード
formatter
# encoding: Shift_JIS require_relative './accepter' =begin rdoc = Formatterクラス =end class Formatter NOT_OVERRRIDE = 'not override error' def format(accepter) raise NOT_OVERRRIDE end end
csv_formatter
# encoding: Shift_JIS require_relative './formatter' =begin rdoc = CsvFormatterクラス =end class CsvFormatter < Formatter def format(accepter) output = "" output << "#{accepter.title},#{accepter.url}\n" return output if accepter.get_children.nil? children = accepter.get_children children.each{|child| output << child.accept(self) } return output end end
hatena_formatter
# encoding: Shift_JIS require_relative './formatter' =begin rdoc = HatenaFormatterクラス =end class HatenaFormatter < Formatter def format(accepter) output = "" output << "[#{accepter.url};title=#{accepter.title}]\n" return output if accepter.get_children.nil? children = accepter.get_children children.each{|child| output << child.accept(self) } return output end end
accepter
# encoding: Shift_JIS require_relative './formatter' =begin rdoc = Accepterクラス =end class Accepter NOT_OVERRRIDE = 'not override error' def accept(formatter) formatter.format(self) end end
web_page
# encoding: Shift_JIS require_relative './accepter' =begin rdoc = WebPageクラス =end class WebPage < Accepter attr_accessor :title,:url,:web_page_children NOT_OVERRRIDE = 'not override error' def initialize() @web_page_children = Array.new end def add(web_page) raise WebPage::NOT_OVERRRIDE end def remove(remove_page) raise WebPage::NOT_OVERRRIDE end def get_children() raise WebPage::NOT_OVERRRIDE end end
link_page
# encoding: Shift_JIS require_relative './web_page' =begin rdoc = WebPageクラス =end class LinkPage < WebPage def add(web_page) @web_page_children.push web_page end def remove(remove_page) web_page.delete remove_page.title end def get_children() return web_page_children end end
terminate_page
# encoding: Shift_JIS require_relative './web_page' =begin rdoc = TerminatePage クラス =end class TerminatePage < WebPage def get_children() return nil end end
main
# encoding: Shift_JIS require_relative './accepter' require_relative './web_page' require_relative './terminate_page' require_relative './link_page' require_relative './csv_formatter' require_relative './hatena_formatter' root_page = LinkPage.new root_page.title = 'ルートページ' root_page.url = 'http://ルートページ.com' link_page1 = LinkPage.new link_page1.title = 'リンクページ1' link_page1.url = 'http://リンクページ1.com' terminate_page1 = TerminatePage.new terminate_page1.title = "末端タイトル1" terminate_page1.url = 'http://末端1.com' terminate_page1_1 = TerminatePage.new terminate_page1_1.title = "末端タイトル1_1" terminate_page1_1.url = 'http://末端1_1.com' link_page2 = LinkPage.new link_page2.title = 'リンクページ2' link_page2.url = 'http://リンクページ2.com' link_page1.add(terminate_page1_1) root_page.add(link_page1) root_page.add(terminate_page1) root_page.add(link_page2) puts "−−−−CSVフォーマット−−−−" csv_formatter = CsvFormatter.new puts csv_formatter.format root_page puts "−−−−Hatenaフォーマット−−−−" hatena_formatter = HatenaFormatter.new puts hatena_formatter.format root_page
出力結果
−−−−CSVフォーマット−−−− ルートページ,http://ルートページ.com リンクページ1,http://リンクページ1.com 末端タイトル1_1,http://末端1_1.com 末端タイトル1,http://末端1.com リンクページ2,http://リンクページ2.com −−−−Hatenaフォーマット−−−− [http://ルートページ.com;title=ルートページ] [http://リンクページ1.com;title=リンクページ1] [http://末端1_1.com;title=末端タイトル1_1] [http://末端1.com;title=末端タイトル1] [http://リンクページ2.com;title=リンクページ2]