Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

RubyでCompositeパターン

概要

GoFデザインパターンCompositeパターンについて。
よくある例としてファイルとフォルダを同様のComponentとして扱い、
再帰処理を可能とする。
木構造を再現する際などに利用。

登場人物

Component = コンポーネント。構成要素
Composite = コンポジット。合成要素
Lief = 葉。再帰しない要素
Client = 利用者

UML


実装サンプル

サンプル概要

Webサイトのリンク構成を収集したいとします。
Webサイト全体はWebPageクラス。
ページにはリンクを保持するページとリンクを保持しないページがあります。
リンクを保持するページはLinkPageクラス。
リンクページはLinkPageか後述のTerminatePageを構成要素として持ちます。
リンクを保持しない終端サイトはTerminatePage。
終端サイトは子要素を持ちません。
Webサイトの収集を行うクローラーをCrawlerクラスとします。

※単純化のためにリンクの相互参照はしないものとします。

登場人物

Component = WebPage : コンポーネント。ウェブページ全般
Composite = LinkPage : コンポジット。リンクページ
Lief = TerminatePage : 葉。再帰しない要素。終端ページ
Client = Crawler : ページ収集者

UML


サンプルコード

WebPage

# encoding: Shift_JIS
=begin rdoc
= WebPageクラス
=end
class WebPage
  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
  
  def get_pages(count_nest)
    raise WebPage::NOT_OVERRRIDE
  end
end

LinkPage

# encoding: Shift_JIS
require_relative './web_page'

=begin rdoc
= LinkPageクラス
=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

  def get_pages(count_nest)
    pages = ""
    count_nest = count_nest + 1
    pages << "[[#{@title}>#{@url}]]\n"
    web_page_children.each {|child|
      pages << "\t"*count_nest
      pages << child.get_pages(count_nest)
    }
    pages << "\n"
    return pages
  end
end

TerminatePage

# encoding: Shift_JIS
require_relative './web_page'

=begin rdoc
= TerminatePageクラス
=end
class TerminatePage < WebPage
  def get_pages(count_nest)
    return "[[#{@title}>#{@url}]]"
  end
end

Crawler

# encoding: Shift_JIS
require_relative './web_page'

=begin rdoc
= Crawlerクラス
=end
class Crawler
  def get_page_tree(root_page)
    return root_page.get_pages(0)
  end
end

main

# encoding: Shift_JIS
require_relative './web_page'
require_relative './terminate_page'
require_relative './link_page'
require_relative './crawler'

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_page1.add(terminate_page1_1)
root_page.add(link_page1)
root_page.add(terminate_page1)

crawler = Crawler.new
puts crawler.get_page_tree root_page
出力結果
[[ルートページ>http://ルートページ.com]]
	[[リンクページ1>http://リンクページ1.com]]
		[[末端タイトル1_1>http://末端1_1.com]]
	[[末端タイトル1>http://末端1.com]]