Rubyでデザインパターン

30分プログラム、その368。Rubyデザインパターンの一部を書いてみた。
前にRubyで学ぶデザインパターンを見たときに、Rubyデザインパターンをやるときは、Javaのをそのまま移植する必要はないなぁ、と感じたのでやってみた。
Rubyだとinterfaceやらabstractは不要なので、そのへんに気をつけてやってみた。

使い方

$ ruby dp.rb
==============================
Iterator pattern
==============================
Around the world in 80 days
Bible
Cinderela
Daddy-Long-Legs
------------------------------

==============================
Adapter(継承)
==============================
(Hello)
*Hello*
------------------------------

==============================
Adapter(移譲)
==============================
368-dp.rb:99: warning: method redefined; discarding old initialize
368-dp.rb:103: warning: method redefined; discarding old showWithParen
368-dp.rb:107: warning: method redefined; discarding old showWithAster
(__FORWARDABLE__):1: warning: method redefined; discarding old printWeak
(__FORWARDABLE__):1: warning: method redefined; discarding old printStrong
368-dp.rb:118: warning: method redefined; discarding old initialize
(Hello)
*Hello*
------------------------------

==============================
Template Method
==============================
<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+
+------------------+
|こんにちは。|
|こんにちは。|
|こんにちは。|
|こんにちは。|
|こんにちは。|
+------------------+
------------------------------

ソースコード

#! /opt/local/bin/ruby -w
# -*- mode:ruby; coding:utf-8 -*-
#
# dp.rb -
#
# Copyright(C) 2008 by mzp
# Author: MIZUNO Hiroki / mzpppp at gmail dot com
# http://howdyworld.org
#
# Timestamp: 2008/09/18 21:50:16
#
# This program is free software; you can redistribute it and/or
# modify it under MIT Lincence.
#
def section(title,&f)
  puts '=============================='
  puts title
  puts '=============================='
  f.call
  puts '------------------------------',''
end

section 'Iterator pattern' do
  class Book
    attr_reader :name
    def initialize(name)
      @name = name
    end
  end

  # 親クラス不要
  class BookShelf
    def initialize(size)
      @books = Array.new size
      @last  = 0
    end

    def appendBook(book)
      @books[@last] = book
      @last += 1
    end

    def each
      @books.each do|book|
        yield book
      end
    end
  end

  bookshelf = BookShelf.new 4
  bookshelf.appendBook Book.new('Around the world in 80 days')
  bookshelf.appendBook Book.new('Bible')
  bookshelf.appendBook Book.new('Cinderela')
  bookshelf.appendBook Book.new('Daddy-Long-Legs')

  bookshelf.each{|book|
    puts book.name
  }
end

section 'Adapter(継承)' do
  class Banner
    def initialize(string)
      @string = string
    end

    def showWithParen
      puts "(#{@string})"
    end

    def showWithAster
      puts "*#{@string}*"
    end
  end

  # Printインタフェースが不要
  class PrintBanner < Banner
    def initialize(string)
      super string
    end

    def printWeak
      showWithParen
    end

    def printStrong
      showWithAster
    end
  end

  # Main
  p = PrintBanner.new 'Hello'
  p.printWeak
  p.printStrong
end

section('Adapter(移譲)') do
  class Banner
    def initialize(string)
      @string = string
    end

    def showWithParen
      puts "(#{@string})"
    end

    def showWithAster
      puts "*#{@string}*"
    end
  end

  require 'forwardable'
  class PrintBanner
    extend Forwardable
    def_delegator :@banner, :showWithParen, :printWeak
    def_delegator :@banner, :showWithAster, :printStrong

    def initialize(string)
      @banner = Banner.new string
    end
  end

  # Main
  p = PrintBanner.new 'Hello'
  p.printWeak
  p.printStrong
end

section('Template Method')do
  class AbstractDisplay
    # Abstractは不要
    def display
      open
      5.times{ output }
      close
    end
  end

  class CharDisplay < AbstractDisplay
    def initialize(ch)
      @ch = ch
    end

    def open
      print '<<'
    end

    def output
      print @ch
    end

    def close
      puts '>>'
    end
  end

  class StringDisplay < AbstractDisplay
    def initialize(string)
      @string = string
      @width =  string.size
    end

    def open
      printLine
    end

    def output
      puts "|#{@string}|" 
    end

    def close
      printLine
    end

    private
    def printLine
      print '+'
      print '-'*@width
      puts '+'
    end
  end

  d1 = CharDisplay.new 'H'
  d2 = StringDisplay.new 'Hello, world.'
  d3 = StringDisplay.new 'こんにちは。'

  d1.display
  d2.display
  d3.display
end