Design Pattern: Ruby Companion

2 AbstractFactory パターン

2.1 AbstractFactoryパターンの意図

(ずばっと略)

2.2 AbstractFactoryパターンの実装

結城浩さんのサンプルを元に,Rubyで実装してみました.

Ruby版では,4つのソースからなります.

「〜factory.rb」というファイルで定義されるクラスは,実はファクトリクラスだけではありません.そのファクトリが生成するパーツ(List,Tray,Pageなど)のクラスも定義しています.

2.2.1 サンプルその1: 普通の実装

素朴にRubyで実装したものです.

class Factory
  def Factory.getFactory(klass)
    begin
      factory = Object.const_get(klass).new
      return factory
    rescue NameError
      print "undefined class: #{klass}.\n"
    end
  end

end

## abstract
class Item
  def initialize(caption)
    @caption = caption
  end
end

## abstract
class Link < Item
  def initialize(caption, url)
    super(caption)
    @url = url
  end    
end

## abstract
class Tray < Item
  def initialize(caption)
    super(caption)
    @tray = Array.new()
  end

  def add(item)
    @tray << item
  end
end

## abstract
class Page
  def initialize(title, author)
    @title, @author = title, author
    @content = Array.new()
  end

  def add(item)
    @content << item
  end

  def output
    begin
      filename = @title + ".html";
      File.open(filename, "w"){|f|
        f.write(makeHTML())
      }
      print "#{filename} was created.\n"
    rescue
      print $!+"\n"
      print $@.join("\n")+"\n"
    end
  end

  def makeHTML
    raise NotImplementedError
  end
end
class ListFactory < Factory

  def createLink(caption, url)
    ListLink.new(caption, url)
  end

  def createTray(caption)
    ListTray.new(caption)
  end

  def createPage(title, author)
    ListPage.new(title, author)
  end
end

class ListLink < Link
  def makeHTML()
    return " <li><a href=\"#{@url}\">#{@caption}</a></li>\n";
  end
end

class ListTray < Tray
  def makeHTML
    items = @tray.collect{|item|
      item.makeHTML
    }.join('')

    buffer = <<EOB
<li>
      #{@caption}
<ul>
      #{items}
</ul>
</il>
EOB

    buffer
  end
end

class ListPage < Page

  def makeHTML()
    items = @content.collect{|item|
      item.makeHTML()
    }.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<ul>
#{items}
</ul>
<hr><address>#{@author}</address>
</body></html>
EOB

    buffer
  end
end
class TableFactory < Factory

  def createLink(caption, url)
    TableLink.new(caption, url)
  end

  def createTray(caption)
    TableTray.new(caption)
  end

  def createPage(title, author)
    TablePage.new(title, author)
  end

end

class TableLink < Link
  def makeHTML
    "<td><a href=\"#{@url}\">#{@caption}</a></td>\n"
  end
end

class TableTray < Tray
  def makeHTML
    items = @tray.collect{|item| item.makeHTML()}.join('')

    buffer = <<"EOB"
<td>
<table width="100%" border="1"><tr>
<td bgcolor="#cccccc" align="center" colspan="#{@tray.size()}">
<b>#{@caption}</b>
</td>
</tr>
<tr>
#{items}
</tr>
</table>
</td>
EOB

    buffer
  end
end


class TablePage < Page

  def makeHTML
    items = @content.collect{|item| "<tr>#{item.makeHTML()}</tr>"}.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<table with="80%" border="3">
#{items}
</table>
<hr>
<address>#{@author}</address>
</body>
</html>
EOB

    buffer
  end
end
require 'factory.rb'
require 'listfactory.rb'
require 'tablefactory.rb'


def usage()
  print "Usage: ruby main.rb <class name of ConcreteFactory>\n"
  print "Example 1: ruby main.rb ListFactory\n"
  print "Example 2: ruby main.rb TableFactory\n"
end


## main
if ARGV.length != 1
  usage()
  exit(0)
end

factory = Factory.getFactory(ARGV[0])

asahi    = factory.createLink("ASAHI newspaper", "http://www.asahi.com/")
yomiuri  = factory.createLink("YOMIYURI newspaper", "http://www.yomiuri.co.jp/")
us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/")
jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp/")
excite   = factory.createLink("Excite", "http://www.excite.com/")
google   = factory.createLink("Google", "http://www.google.com/")

traynews = factory.createTray("Newspaper")
traynews.add(asahi)
traynews.add(yomiuri)

trayyahoo = factory.createTray("Yahoo!")
trayyahoo.add(us_yahoo)
trayyahoo.add(jp_yahoo)

traysearch = factory.createTray("Search Engine")
traysearch.add(trayyahoo)
traysearch.add(excite)
traysearch.add(google)

page = factory.createPage("LinkPage", "YUKI, Hiroshi")
page.add(traynews)
page.add(traysearch)

page.output()

2.2.2 サンプルその2: Constantメソッド

「Constant Method」というパターン(イディオム)を使っています.これは,Template Methodパターンの一種で,メソッドの返り値として「クラスそのもの」を表すオブジェクトを返すようにするものです.

## Constant Method Solution
## DPSC p.38

class Factory
  def Factory.getFactory(klass)
    begin
      factory = Object.const_get(klass).new
      return factory
    rescue NameError
      print "undefined class: #{klass}\n"
    rescue
      raise
    end
  end

  def createLink(*args)
    linkClass.new(*args)
  end

  def createTray(*args)
    trayClass.new(*args)
  end

  def createPage(*args)
    pageClass.new(*args)
  end

end

## abstract
class Item
  def initialize(caption)
    @caption = caption
  end
end

## abstract
class Link < Item
  def initialize(caption, url)
    super(caption)
    @url = url
  end    
end

## abstract
class Tray < Item
  def initialize(caption)
    super(caption)
    @tray = Array.new()
  end

  def add(item)
    @tray << item
  end
end

## abstract
class Page
  def initialize(title, author)
    @title, @author = title, author
    @content = Array.new()
  end

  def add(item)
    @content << item
  end

  def output
    begin
      filename = @title + ".html";
      File.open(filename, "w"){|f|
        f.write(makeHTML())
      }
      print "#{filename} was created.\n"
    rescue
      print $!+"\n"
      print $@.join("\n")+"\n"
    end
  end

  def makeHTML
    raise NotImplementedError
  end
end
class ListFactory < Factory

  def linkClass()
    ListLink
  end

  def trayClass()
    ListTray
  end

  def pageClass()
    ListPage
  end

end

class ListLink < Link
  def makeHTML()
    return " <li><a href=\"#{@url}\">#{@caption}</a></li>\n";
  end
end

class ListTray < Tray
  def makeHTML
    items = @tray.collect{|item|
      item.makeHTML
    }.join('')

    buffer = <<EOB
<li>
      #{@caption}
<ul>
      #{items}
</ul>
</il>
EOB

    buffer
  end
end

class ListPage < Page

  def makeHTML()
    items = @content.collect{|item|
      item.makeHTML()
    }.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<ul>
#{items}
</ul>
<hr><address>#{@author}</address>
</body></html>
EOB

    buffer
  end
end
class TableFactory < Factory

  def linkClass()
    TableLink
  end

  def trayClass()
    TableTray
  end

  def pageClass()
    TablePage
  end

end

class TableLink < Link
  def makeHTML
    "<td><a href=\"#{@url}\">#{@caption}</a></td>\n"
  end
end

class TableTray < Tray
  def makeHTML
    items = @tray.collect{|item| item.makeHTML()}.join('')

    buffer = <<"EOB"
<td>
<table width="100%" border="1"><tr>
<td bgcolor="#cccccc" align="center" colspan="#{@tray.size()}">
<b>#{@caption}</b>
</td>
</tr>
<tr>
#{items}
</tr>
</table>
</td>
EOB

    buffer
  end
end


class TablePage < Page

  def makeHTML
    items = @content.collect{|item| "<tr>#{item.makeHTML()}</tr>"}.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<table with="80%" border="3">
#{items}
</table>
<hr>
<address>#{@author}</address>
</body>
</html>
EOB

    buffer
  end
end
require 'factory.rb'
require 'listfactory.rb'
require 'tablefactory.rb'


def usage()
  print "Usage: ruby main.rb <class name of ConcreteFactory>\n"
  print "Example 1: ruby main.rb ListFactory\n"
  print "Example 2: ruby main.rb TableFactory\n"
end


## main
if ARGV.length != 1
  usage()
  exit(0)
end

factory = Factory.getFactory(ARGV[0])

asahi    = factory.createLink("ASAHI newspaper", "http://www.asahi.com/")
yomiuri  = factory.createLink("YOMIYURI newspaper", "http://www.yomiuri.co.jp/")
us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/")
jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp/")
excite   = factory.createLink("Excite", "http://www.excite.com/")
google   = factory.createLink("Google", "http://www.google.com/")

traynews = factory.createTray("Newspaper")
traynews.add(asahi)
traynews.add(yomiuri)

trayyahoo = factory.createTray("Yahoo!")
trayyahoo.add(us_yahoo)
trayyahoo.add(jp_yahoo)

traysearch = factory.createTray("Search Engine")
traysearch.add(trayyahoo)
traysearch.add(excite)
traysearch.add(google)

page = factory.createPage("LinkPage", "YUKI, Hiroshi")
page.add(traynews)
page.add(traysearch)

page.output()

2.2.3 サンプルその3: パーツカタログ

「パーツカタログ」というイディオムを使っています.これは,ファクトリのインスタンスにハッシュオブジェクトを持たせます.ハッシュには,キーとして生成したいものを表すシンボルを,値としてそれに対応するクラスのクラスオブジェクトを,それぞれ与えておきます.そして,各オブジェクトを生成する際には,そのファクトリが持っているハッシュの値にnewメソッドを適用させ,インスタンスを作ります.

Factoryのサブクラスでは,インスタンス変数を1つ定義しておくだけです.メソッドはスーパークラスのものをそのまま継承して使うことになります.

## partsCatalog
## DPSC p.xx

class Factory
  def Factory.getFactory(klass)
    begin
      factory = Object.const_get(klass).new
      return factory
    rescue NameError
      print "undefined class: #{klass}\n"
    end
  end

  def initialize
    @partsCatalog = nil
  end

  def create(part, *args)
    @partsCatalog[part].new(*args)
  end

end

## abstract
class Item
  def initialize(caption)
    @caption = caption
  end
end

## abstract
class Link < Item
  def initialize(caption, url)
    super(caption)
    @url = url
  end    
end

## abstract
class Tray < Item
  def initialize(caption)
    super(caption)
    @tray = Array.new()
  end

  def add(item)
    @tray << item
  end
end

## abstract
class Page
  def initialize(title, author)
    @title, @author = title, author
    @content = Array.new()
  end

  def add(item)
    @content << item
  end

  def output
    begin
      filename = @title + ".html";
      File.open(filename, "w"){|f|
        f.write(makeHTML())
      }
      print "#{filename} was created.\n"
    rescue
      print $!+"\n"
      print $@.join("\n")+"\n"
    end
  end

  def makeHTML
    raise NotImplementedError
  end
end
class ListFactory < Factory

  def initialize
    @partsCatalog = {
      :Link => ListLink,
      :Tray => ListTray,
      :Page => ListPage,
    }
  end

end

class ListLink < Link
  def makeHTML()
    return " <li><a href=\"#{@url}\">#{@caption}</a></li>\n";
  end
end

class ListTray < Tray
  def makeHTML
    items = @tray.collect{|item|
      item.makeHTML
    }.join('')

    buffer = <<EOB
<li>
      #{@caption}
<ul>
      #{items}
</ul>
</il>
EOB

    buffer
  end
end

class ListPage < Page

  def makeHTML()
    items = @content.collect{|item|
      item.makeHTML()
    }.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<ul>
#{items}
</ul>
<hr><address>#{@author}</address>
</body></html>
EOB

    buffer
  end
end
class TableFactory < Factory

  def initialize
    @partsCatalog = {
      :Link => TableLink,
      :Tray => TableTray,
      :Page => TablePage,
    }
  end

end

class TableLink < Link
  def makeHTML
    "<td><a href=\"#{@url}\">#{@caption}</a></td>\n"
  end
end

class TableTray < Tray
  def makeHTML
    items = @tray.collect{|item| item.makeHTML()}.join('')

    buffer = <<"EOB"
<td>
<table width="100%" border="1"><tr>
<td bgcolor="#cccccc" align="center" colspan="#{@tray.size()}">
<b>#{@caption}</b>
</td>
</tr>
<tr>
#{items}
</tr>
</table>
</td>
EOB

    buffer
  end
end


class TablePage < Page

  def makeHTML
    items = @content.collect{|item| "<tr>#{item.makeHTML()}</tr>"}.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<table with="80%" border="3">
#{items}
</table>
<hr>
<address>#{@author}</address>
</body>
</html>
EOB

    buffer
  end
end
require 'factory.rb'
require 'listfactory.rb'
require 'tablefactory.rb'


def usage()
  print "Usage: ruby main.rb <class name of ConcreteFactory>\n"
  print "Example 1: ruby main.rb ListFactory\n"
  print "Example 2: ruby main.rb TableFactory\n"
end


## main
if ARGV.length != 1
  usage()
  exit(0)
end

factory = Factory.getFactory(ARGV[0])

asahi    = factory.create(:Link, "ASAHI newspaper", "http://www.asahi.com/")
yomiuri  = factory.create(:Link, "YOMIURI newspaper", "http://www.yomiuri.co.jp/")
us_yahoo = factory.create(:Link, "Yahoo!", "http://www.yahoo.com/")
jp_yahoo = factory.create(:Link, "Yahoo!Japan", "http://www.yahoo.co.jp/")
excite   = factory.create(:Link, "Excite", "http://www.excite.com/")
google   = factory.create(:Link, "Google", "http://www.google.com/")

traynews = factory.create(:Tray, "Newspaper")
traynews.add(asahi)
traynews.add(yomiuri)

trayyahoo = factory.create(:Tray, "Yahoo!")
trayyahoo.add(us_yahoo)
trayyahoo.add(jp_yahoo)

traysearch = factory.create(:Tray, "Search Engine")
traysearch.add(trayyahoo)
traysearch.add(excite)
traysearch.add(google)

page = factory.create(:Page, "LinkPage", "YUKI, Hiroshi")
page.add(traynews)
page.add(traysearch)

page.output()

2.2.4 サンプルその3a: クラスインスタンス変数によるパーツカタログ

パーツカタログをクラスインスタンス変数を使って実装したものです.先ほどの例とは違い,カタログを持っているのは各ファクトリクラスになります.

## partsCatalog as Class Instance Variable
## DPSC p.xx

class Factory

  @partsCatalog = nil

  def self.partsCatalog()
    @partsCatalog
  end

  def Factory.getFactory(klass)
    begin
      factory = Object.const_get(klass).new
      return factory
    rescue NameError
      print "undefined class: #{klass}\n"
    end
  end

  def create(part, *args)
    self.class.partsCatalog[part].new(*args)
  end

end

## abstract
class Item
  def initialize(caption)
    @caption = caption
  end
end

## abstract
class Link < Item
  def initialize(caption, url)
    super(caption)
    @url = url
  end    
end

## abstract
class Tray < Item
  def initialize(caption)
    super(caption)
    @tray = Array.new()
  end

  def add(item)
    @tray << item
  end
end

## abstract
class Page
  def initialize(title, author)
    @title, @author = title, author
    @content = Array.new()
  end

  def add(item)
    @content << item
  end

  def output
    begin
      filename = @title + ".html";
      File.open(filename, "w"){|f|
        f.write(makeHTML())
      }
      print "#{filename} was created.\n"
    rescue
      print $!+"\n"
      print $@.join("\n")+"\n"
    end
  end

  def makeHTML
    raise NotImplementedError
  end
end
class ListLink < Link
  def makeHTML()
    return " <li><a href=\"#{@url}\">#{@caption}</a></li>\n";
  end
end

class ListTray < Tray
  def makeHTML
    items = @tray.collect{|item|
      item.makeHTML
    }.join('')

    buffer = <<EOB
<li>
      #{@caption}
<ul>
      #{items}
</ul>
</il>
EOB

    buffer
  end
end

class ListPage < Page

  def makeHTML()
    items = @content.collect{|item|
      item.makeHTML()
    }.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<ul>
#{items}
</ul>
<hr><address>#{@author}</address>
</body></html>
EOB

    buffer
  end
end

class ListFactory < Factory

  @partsCatalog = {
    :Link => ListLink,
    :Tray => ListTray,
    :Page => ListPage
  }

end
class TableLink < Link
  def makeHTML
    "<td><a href=\"#{@url}\">#{@caption}</a></td>\n"
  end
end

class TableTray < Tray
  def makeHTML
    items = @tray.collect{|item| item.makeHTML()}.join('')

    buffer = <<"EOB"
<td>
<table width="100%" border="1"><tr>
<td bgcolor="#cccccc" align="center" colspan="#{@tray.size()}">
<b>#{@caption}</b>
</td>
</tr>
<tr>
#{items}
</tr>
</table>
</td>
EOB

    buffer
  end
end


class TablePage < Page

  def makeHTML
    items = @content.collect{|item| "<tr>#{item.makeHTML()}</tr>"}.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<table with="80%" border="3">
#{items}
</table>
<hr>
<address>#{@author}</address>
</body>
</html>
EOB

    buffer
  end
end

class TableFactory < Factory

  @partsCatalog = {
    :Link => TableLink,
    :Tray => TableTray,
    :Page => TablePage,
  }

end
require 'factory.rb'
require 'listfactory.rb'
require 'tablefactory.rb'


def usage()
  print "Usage: ruby main.rb <class name of ConcreteFactory>\n"
  print "Example 1: ruby main.rb ListFactory\n"
  print "Example 2: ruby main.rb TableFactory\n"
end


## main
if ARGV.length != 1
  usage()
  exit(0)
end

factory = Factory.getFactory(ARGV[0])

asahi    = factory.create(:Link, "ASAHI newspaper", "http://www.asahi.com/")
yomiuri  = factory.create(:Link, "YOMIURI newspaper", "http://www.yomiuri.co.jp/")
us_yahoo = factory.create(:Link, "Yahoo!", "http://www.yahoo.com/")
jp_yahoo = factory.create(:Link, "Yahoo!Japan", "http://www.yahoo.co.jp/")
excite   = factory.create(:Link, "Excite", "http://www.excite.com/")
google   = factory.create(:Link, "Google", "http://www.google.com/")

traynews = factory.create(:Tray, "Newspaper")
traynews.add(asahi)
traynews.add(yomiuri)

trayyahoo = factory.create(:Tray, "Yahoo!")
trayyahoo.add(us_yahoo)
trayyahoo.add(jp_yahoo)

traysearch = factory.create(:Tray, "Search Engine")
traysearch.add(trayyahoo)
traysearch.add(excite)
traysearch.add(google)

page = factory.create(:Page, "LinkPage", "YUKI, Hiroshi")
page.add(traynews)
page.add(traysearch)

page.output()

2.2.5 サンプルその4: 命名の規約によるファクトリ

ファクトリのサブクラスではもはや何も定義しません.クラスそのものを定義するだけです.その代わり,各パーツのクラスは同じ規約に従って,名前を揃えておく必要があります.例えば, LinkクラスをListFactoryとTableFactoryで生成するなら,それぞれListLink,TableLinkとする,という具合いです.これはつまり,ファクトリのクラス名が決まると,それが生成するパーツの名前が決まってしまう,ということです.

もっとも,すでに使ってきたサンプルでも,クラスの名前はこのような規則に従ってつけられてきたので,クラス名を大変更しなければいけない,ということはありません.

この実装は,使用するパーツのクラス名がソース内のどこにも書かれなくなるため,ソースコードが少し分かりにくくなるかもしれません.コメントなどで注意を払う必要があるかもしれません.

## Single Factory Class
## DPSC p.xx

class Factory
  def Factory.getFactory(klass)
    begin
      factory = Object.const_get(klass).new
      return factory
    rescue NameError
      print "undefined class: #{klass}\n"
    end
  end

  def create(part, *args)
    klassname = self.type.to_s.sub(/Factory$/, part.to_s)
    Object.const_get(klassname).new(*args)
  end

end

## abstract
class Item
  def initialize(caption)
    @caption = caption
  end
end

## abstract
class Link < Item
  def initialize(caption, url)
    super(caption)
    @url = url
  end    
end

## abstract
class Tray < Item
  def initialize(caption)
    super(caption)
    @tray = Array.new()
  end

  def add(item)
    @tray << item
  end
end

## abstract
class Page
  def initialize(title, author)
    @title, @author = title, author
    @content = Array.new()
  end

  def add(item)
    @content << item
  end

  def output
    begin
      filename = @title + ".html";
      File.open(filename, "w"){|f|
        f.write(makeHTML())
      }
      print "#{filename} was created.\n"
    rescue
      print $!+"\n"
      print $@.join("\n")+"\n"
    end
  end

  def makeHTML
    raise NotImplementedError
  end
end
class ListFactory < Factory
end

class ListLink < Link
  def makeHTML()
    return " <li><a href=\"#{@url}\">#{@caption}</a></li>\n";
  end
end

class ListTray < Tray
  def makeHTML
    items = @tray.collect{|item|
      item.makeHTML
    }.join('')

    buffer = <<EOB
<li>
      #{@caption}
<ul>
      #{items}
</ul>
</il>
EOB

    buffer
  end
end

class ListPage < Page

  def makeHTML()
    items = @content.collect{|item|
      item.makeHTML()
    }.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<ul>
#{items}
</ul>
<hr><address>#{@author}</address>
</body></html>
EOB

    buffer
  end
end
class TableFactory < Factory
end

class TableLink < Link
  def makeHTML
    "<td><a href=\"#{@url}\">#{@caption}</a></td>\n"
  end
end

class TableTray < Tray
  def makeHTML
    items = @tray.collect{|item| item.makeHTML()}.join('')

    buffer = <<"EOB"
<td>
<table width="100%" border="1"><tr>
<td bgcolor="#cccccc" align="center" colspan="#{@tray.size()}">
<b>#{@caption}</b>
</td>
</tr>
<tr>
#{items}
</tr>
</table>
</td>
EOB

    buffer
  end
end


class TablePage < Page

  def makeHTML
    items = @content.collect{|item| "<tr>#{item.makeHTML()}</tr>"}.join('')

    buffer = <<EOB
<html><head><title>#{@title}</title></head>
<body>
<h1>#{@title}</h1>
<table with="80%" border="3">
#{items}
</table>
<hr>
<address>#{@author}</address>
</body>
</html>
EOB

    buffer
  end
end
require 'factory.rb'
require 'listfactory.rb'
require 'tablefactory.rb'


def usage()
  print "Usage: ruby main.rb <class name of ConcreteFactory>\n"
  print "Example 1: ruby main.rb ListFactory\n"
  print "Example 2: ruby main.rb TableFactory\n"
end


## main
if ARGV.length != 1
  usage()
  exit(0)
end

factory = Factory.getFactory(ARGV[0])

asahi    = factory.create(:Link, "ASAHI newspaper", "http://www.asahi.com/")
yomiuri  = factory.create(:Link, "YOMIURI newspaper", "http://www.yomiuri.co.jp/")
us_yahoo = factory.create(:Link, "Yahoo!", "http://www.yahoo.com/")
jp_yahoo = factory.create(:Link, "Yahoo!Japan", "http://www.yahoo.co.jp/")
excite   = factory.create(:Link, "Excite", "http://www.excite.com/")
google   = factory.create(:Link, "Google", "http://www.google.com/")

traynews = factory.create(:Tray, "Newspaper")
traynews.add(asahi)
traynews.add(yomiuri)

trayyahoo = factory.create(:Tray, "Yahoo!")
trayyahoo.add(us_yahoo)
trayyahoo.add(jp_yahoo)

traysearch = factory.create(:Tray, "Search Engine")
traysearch.add(trayyahoo)
traysearch.add(excite)
traysearch.add(google)

page = factory.create(:Page, "LinkPage", "YUKI, Hiroshi")
page.add(traynews)
page.add(traysearch)

page.output()

2.3 おまけ

Abstract Factoryパターンというのは,たいして大きくないものを作る時には重要じゃないような気がします.逆に融通はきかなくなりますし.一方,何かのフレームワークを作る時など,同じパターンの部品を別々のクラスで作りたい場合には有用のようです.

2.4 ソース

固めておきました.こちらからどうぞ.

<URL:src-abstractfactory.tar.gz>

2.5 (ソースのライセンス)

このソースは,結城浩さんによる『Java言語で学ぶデザインパターン入門』を元に,たかはしが Ruby用に手を入れたものです.Rubyとして自然なソースにするようにしたため,あんまり原型を留めてません.

オリジナルのソースのライセンスは, <URL:http://www.hyuki.com/dp/index.html#download> にあります.このソースの扱いも上記と同様でお願いします.

文責: たかはし(maki@rubycolor.org)