『増補改訂版 Java言語で学ぶデザインパターン入門』(結城 浩, 2004)にあるデザインパターンを 学んだ頃 Ruby に慣れたいと思ってたので、 Ruby で各デザインパターンを実装した.
※2012年8月頃に書いたコードをまとめたもの. 今見返すと当時はあまり理解してなかった内容も多く, ぜひもう一度手を動かしながら読み返したい本.
| #!/usr/bin/ruby -Ku | |
| class Aggregate | |
| def iterator | |
| # pass | |
| end | |
| end | |
| class Iterator | |
| def has_next | |
| # pass | |
| end | |
| def next | |
| # pass | |
| end | |
| end | |
| class Book | |
| def initialize(name) | |
| @name = name | |
| end | |
| def get_name | |
| return @name | |
| end | |
| end | |
| class BookShelf | |
| def initialize(maxsize) | |
| @books = Array.new(maxsize) | |
| @last = 0 | |
| end | |
| def get_book_at(index) | |
| return @books[index] | |
| end | |
| def append_book(book) | |
| @books[@last] = book | |
| @last += 1 | |
| end | |
| def get_length | |
| return @last | |
| end | |
| def iterator | |
| return BookShelfIterator.new(self) | |
| end | |
| end | |
| class BookShelfIterator | |
| def initialize(book_shelf) | |
| @book_shelf = book_shelf | |
| @index = 0 | |
| end | |
| def has_next? | |
| if @index < @book_shelf.get_length | |
| return true | |
| else | |
| return false | |
| end | |
| end | |
| def next | |
| @book = @book_shelf.get_book_at(@index) | |
| @index += 1 | |
| return @book | |
| end | |
| end | |
| def main | |
| book_shelf = BookShelf.new(4) | |
| book_shelf.append_book(Book.new("Around the World in 80 Days")) | |
| book_shelf.append_book(Book.new("Bible")) | |
| book_shelf.append_book(Book.new("Cinderella")) | |
| book_shelf.append_book(Book.new("Dady-Long-Legs")) | |
| it = book_shelf.iterator | |
| while it.has_next? | |
| book = it.next | |
| puts book.get_name | |
| end | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Banner | |
| def initialize(string) | |
| @string = string | |
| end | |
| def show_with_paren | |
| puts "(#@string)" | |
| end | |
| def show_with_aster | |
| puts "*#@string*" | |
| end | |
| end | |
| class Print | |
| def print_weak | |
| # pass | |
| end | |
| def print_strong | |
| # pass | |
| end | |
| end | |
| class PrintBanner < Banner | |
| def print_weak | |
| show_with_paren | |
| end | |
| def print_strong | |
| show_with_aster | |
| end | |
| end | |
| def main | |
| pr = PrintBanner.new("Hello") | |
| pr.print_weak | |
| pr.print_strong | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Banner | |
| def initialize(string) | |
| @string = string | |
| end | |
| def show_with_paren | |
| puts "(#@string)" | |
| end | |
| def show_with_aster | |
| puts "*#@string*" | |
| end | |
| end | |
| class Print | |
| def print_weak | |
| # pass | |
| end | |
| def print_strong | |
| # pass | |
| end | |
| end | |
| class PrintBanner < Print | |
| def initialize(string) | |
| @banner = Banner.new(string) | |
| end | |
| def print_weak | |
| @banner.show_with_paren | |
| end | |
| def print_strong | |
| @banner.show_with_aster | |
| end | |
| end | |
| def main | |
| pr = PrintBanner.new("Hello") | |
| pr.print_weak | |
| pr.print_strong | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| require './string-width' | |
| class AbstractDisplay | |
| def open | |
| # pass | |
| end | |
| def put | |
| # pass | |
| end | |
| def close | |
| # pass | |
| end | |
| def display | |
| open | |
| 5.times do | |
| put | |
| end | |
| close | |
| end | |
| end | |
| class CharDisplay < AbstractDisplay | |
| def initialize(ch) | |
| @ch = ch | |
| end | |
| def open | |
| print "<<" | |
| end | |
| def put | |
| print @ch | |
| end | |
| def close | |
| puts ">>" | |
| end | |
| end | |
| class StringDisplay < AbstractDisplay | |
| def initialize(string) | |
| @string = string | |
| @width = string.width | |
| end | |
| def open | |
| print_line | |
| end | |
| def put | |
| puts "|#@string|" | |
| end | |
| def close | |
| print_line | |
| end | |
| def print_line | |
| puts "+#{'-' * @width}+" | |
| end | |
| end | |
| def main | |
| d1 = CharDisplay.new("H") | |
| d2 = StringDisplay.new("Hello, world.") | |
| d3 = StringDisplay.new("こんにちは。") | |
| d1.display | |
| d2.display | |
| d3.display | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Product | |
| def use | |
| # pass | |
| end | |
| end | |
| class Factory | |
| def create(owner) | |
| @p = create_product(owner) | |
| register_product(@p) | |
| return @p | |
| end | |
| def create_product(owner) | |
| # pass | |
| end | |
| protected :create_product | |
| def register_product(product) | |
| # pass | |
| end | |
| protected :register_product | |
| end | |
| class IDCard < Product | |
| def initialize(owner) | |
| puts "#{owner}のカードを作ります。" | |
| @owner = owner | |
| end | |
| def use | |
| puts "#{@owner}のカードを使います。" | |
| end | |
| def get_owner | |
| return @owner | |
| end | |
| end | |
| class IDCardFactory < Factory | |
| def initialize() | |
| super | |
| @owners = [] | |
| end | |
| def create_product(owner) | |
| return IDCard.new(owner) | |
| end | |
| def register_product(product) | |
| @owners.push(product.get_owner) | |
| end | |
| protected :register_product | |
| def get_owners | |
| return @owners | |
| end | |
| end | |
| def main | |
| factory = IDCardFactory.new | |
| card1 = factory.create("結城浩") | |
| card2 = factory.create("とむら") | |
| card3 = factory.create("佐藤花子") | |
| card1.use | |
| card2.use | |
| card2.use | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Singleton | |
| @@singleton = new | |
| def initialize | |
| puts "インスタンスを生成しました。" | |
| end | |
| def self.get_instance | |
| return @@singleton | |
| end | |
| private_class_method :new | |
| end | |
| def main | |
| puts "Start." | |
| obj1 = Singleton.get_instance | |
| obj2 = Singleton.get_instance | |
| if obj1 == obj2 | |
| puts "obj1とobj2は同じインスタンスです。" | |
| else | |
| puts "obj1とobj2は同じインスタンスではありません。" | |
| end | |
| puts "End." | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| require './string-width' | |
| class Product | |
| def use(s) | |
| # pass | |
| end | |
| def create_clone | |
| # pass | |
| end | |
| end | |
| class Manager | |
| def initialize | |
| @showcase = {} | |
| end | |
| def register(name, proto) | |
| @showcase.store(name, proto) | |
| end | |
| def create(protoname) | |
| pr = @showcase[protoname] | |
| return pr.create_clone | |
| end | |
| end | |
| class MessageBox | |
| def initialize(decochar) | |
| @decochar = decochar | |
| end | |
| def use(s) | |
| length = s.width | |
| puts "#{@decochar * (length + 4)}" | |
| puts "#@decochar #{s} #@decochar" | |
| puts "#{@decochar * (length + 4)}" | |
| end | |
| def create_clone | |
| pr = nil | |
| begin | |
| pr = clone | |
| rescue => e | |
| p e | |
| end | |
| return pr | |
| end | |
| end | |
| class UnderlinePen | |
| def initialize(ulchar) | |
| @ulchar = ulchar | |
| end | |
| def use(s) | |
| length = s.width | |
| puts %{"#{s}"} | |
| puts " #{@ulchar * length}" | |
| end | |
| def create_clone | |
| pr = nil | |
| begin | |
| pr = clone | |
| rescue => e | |
| p e | |
| end | |
| return pr | |
| end | |
| end | |
| def main | |
| manager = Manager.new | |
| upen = UnderlinePen.new("~") | |
| mbox = MessageBox.new("*") | |
| sbox = MessageBox.new("/") | |
| manager.register("strong message", upen) | |
| manager.register("warning box", mbox) | |
| manager.register("slash box", sbox) | |
| p1 = manager.create("strong message") | |
| p1.use("Hello, world.") | |
| p2 = manager.create("warning box") | |
| p2.use("Hello, world.") | |
| p3 = manager.create("slash box") | |
| p3.use("Hello, world.") | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Builder | |
| def make_title(title) | |
| # pass | |
| end | |
| def make_string(string) | |
| # pass | |
| end | |
| def make_items(items) | |
| # pass | |
| end | |
| def close | |
| # pass | |
| end | |
| end | |
| class Director | |
| def initialize(builder) | |
| @builder = builder | |
| end | |
| def construct | |
| @builder.make_title("Greeting") | |
| @builder.make_string("朝から昼にかけて") | |
| @builder.make_items(%w{おはようございます。 こんにちは。}) | |
| @builder.make_string("夜に") | |
| @builder.make_items(%w{こんばんは。 おやすみなさい。 さようなら。}) | |
| @builder.close | |
| end | |
| end | |
| class TextBuilder < Builder | |
| def initialize | |
| @buffer = "" | |
| end | |
| def make_title(title) | |
| @buffer << "==============================\n" | |
| @buffer << "『#{title}』\n" | |
| @buffer << "\n" | |
| end | |
| def make_string(str) | |
| @buffer << "■#{str}\n" | |
| @buffer << "\n" | |
| end | |
| def make_items(items) | |
| items.each do |i| | |
| @buffer << " ・#{i}\n" | |
| end | |
| @buffer << "\n" | |
| end | |
| def close | |
| @buffer << "==============================\n" | |
| end | |
| def get_result | |
| return @buffer | |
| end | |
| end | |
| class HTMLBuilder < Builder | |
| def make_title(title) | |
| @filename = "#{title}.html" | |
| begin | |
| @writer = open(@filename, "w") | |
| rescue => e | |
| p e | |
| end | |
| @writer.write("<html><head><title>#{title}</title></head><body>") | |
| @writer.write("<h1>#{title}</h1>") | |
| end | |
| def make_string(str) | |
| @writer.write("<p>#{str}</p>") | |
| end | |
| def make_items(items) | |
| @writer.write("<ul>") | |
| items.each do |i| | |
| @writer.write("<li>#{i}</li>") | |
| end | |
| @writer.write("</ul>") | |
| end | |
| def close | |
| @writer.write("</body></html>") | |
| @writer.close | |
| end | |
| def get_result | |
| return @filename | |
| end | |
| end | |
| def main(args) | |
| if args.length != 1 | |
| usage | |
| exit(0) | |
| end | |
| if args[0] == "plain" | |
| textbuilder = TextBuilder.new | |
| director = Director.new(textbuilder) | |
| director.construct | |
| result = textbuilder.get_result | |
| puts result | |
| elsif args[0] == "html" | |
| htmlbuilder = HTMLBuilder.new | |
| director = Director.new(htmlbuilder) | |
| director.construct | |
| filename = htmlbuilder.get_result | |
| puts "#{filename} が作成されました。" | |
| else | |
| usage | |
| exit(0) | |
| end | |
| end | |
| def usage | |
| puts "Usage: ruby #$0 plain プレーンテキストで文書作成" | |
| puts "Usage: ruby #$0 html HTMLファイルで文書作成" | |
| end | |
| main(ARGV) |
| #!/usr/bin/ruby -Ku | |
| class Item | |
| def initialize(caption) | |
| @caption = caption | |
| end | |
| def make_html | |
| # pass | |
| end | |
| end | |
| class Link < Item | |
| def initialize(caption, url) | |
| super(caption) | |
| @url = url | |
| end | |
| end | |
| class Tray < Item | |
| def initialize(caption) | |
| super | |
| @tray = [] | |
| end | |
| def add(item) | |
| @tray.push(item) | |
| end | |
| end | |
| class Page | |
| def initialize(title, author) | |
| @title = title | |
| @author = author | |
| @content = [] | |
| end | |
| def add(item) | |
| @content.push(item) | |
| end | |
| def output | |
| begin | |
| filename = "#@title.html" | |
| writer = open(filename, "w") | |
| writer.write(self.make_html) | |
| writer.close | |
| puts "#{filename} を作成しました。" | |
| rescue => e | |
| p e | |
| end | |
| end | |
| def make_html | |
| # pass | |
| end | |
| end | |
| class Factory | |
| def self.get_factory(classname) | |
| factory = nil | |
| begin | |
| factory = const_get(classname).new | |
| rescue => e | |
| p e | |
| end | |
| return factory | |
| end | |
| def create_link(caption, url) | |
| # pass | |
| end | |
| def create_tray(caption) | |
| # pass | |
| end | |
| def create_page(title, author) | |
| # pass | |
| end | |
| end | |
| class ListFactory < Factory | |
| def create_link(caption, url) | |
| return ListLink.new(caption, url) | |
| end | |
| def create_tray(caption) | |
| return ListTray.new(caption) | |
| end | |
| def create_page(title, author) | |
| return ListPage.new(title, author) | |
| end | |
| end | |
| class ListLink < Link | |
| def initialize(caption, url) | |
| super | |
| end | |
| def make_html | |
| return %{ <li><a href="#@url">#@caption</a></li>\n} | |
| end | |
| end | |
| class ListTray < Tray | |
| def initialize(caption) | |
| super | |
| end | |
| def make_html | |
| buffer = "" | |
| buffer << "<li>\n" | |
| buffer << "#@caption\n" | |
| buffer << "<ul>\n" | |
| @tray.each do |item| | |
| buffer << item.make_html | |
| end | |
| buffer << "</ul>\n" | |
| buffer << "</li>\n" | |
| return buffer | |
| end | |
| end | |
| class ListPage < Page | |
| def initialize(title, author) | |
| super | |
| end | |
| def make_html | |
| buffer = "" | |
| buffer << "<html><head><title>#@title</title></head>\n" | |
| buffer << "<body>\n" | |
| buffer << "<h1>#@title</h1>\n" | |
| @content.each do |item| | |
| buffer << item.make_html | |
| end | |
| buffer << "</ul>\n" | |
| buffer << "<hr><address>#@author</address>" | |
| buffer << "</body></html>\n" | |
| return buffer | |
| end | |
| end | |
| class TableFactory < Factory | |
| def create_link(caption, url) | |
| return TableLink.new(caption, url) | |
| end | |
| def create_tray(caption) | |
| return TableTray.new(caption) | |
| end | |
| def create_page(title, author) | |
| return TablePage.new(title, author) | |
| end | |
| end | |
| class TableLink < Link | |
| def initialize(caption, url) | |
| super | |
| end | |
| def make_html | |
| return %{<td><a href="#@url">#@caption</a></td>\n} | |
| end | |
| end | |
| class TableTray < Tray | |
| def initialize(caption) | |
| super | |
| end | |
| def make_html | |
| buffer = "" | |
| buffer << "<td>" | |
| buffer << '<table width="100%" border="1"><tr>' | |
| buffer << %{<td bgcolor="#cccccc" align="center" colspan="#{@tray.size}"><b>#@caption</b></td>} | |
| buffer << "</tr>\n" | |
| buffer << "<tr>\n" | |
| @tray.each do |item| | |
| buffer << item.make_html | |
| end | |
| buffer << "</tr></table>" | |
| buffer << "</td>" | |
| return buffer | |
| end | |
| end | |
| class TablePage < Page | |
| def initialize(title, author) | |
| super | |
| end | |
| def make_html | |
| buffer = "" | |
| buffer << "<html><head><title>#@title</title></head>\n" | |
| buffer << "<body>\n" | |
| buffer << "<h1>#@title</h1>\n" | |
| buffer << %{<table width="80%" border="3">\n} | |
| @content.each do |item| | |
| buffer << "<tr>#{item.make_html}</tr>" | |
| end | |
| buffer << "</table>\n" | |
| buffer << "<hr><address>#@author</address>" | |
| buffer << "</body></html>\n" | |
| return buffer | |
| end | |
| end | |
| def main(args) | |
| if args.length != 1 | |
| puts "Usage: ruby #$0 Class.Name.Of.ConcreteFactory" | |
| puts "Example 1: ruby #$0 ListFactory" | |
| puts "Example 2: ruby #$0 TableFactory" | |
| exit(0) | |
| end | |
| factory = Factory.get_factory(args[0]) | |
| asahi = factory.create_link("朝日新聞", "http://www.asahi.com/") | |
| yomiuri = factory.create_link("読売新聞", "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("新聞") | |
| traynews.add(asahi) | |
| traynews.add(yomiuri) | |
| trayyahoo = factory.create_tray("Yahoo!") | |
| trayyahoo.add(us_yahoo) | |
| trayyahoo.add(jp_yahoo) | |
| traysearch = factory.create_tray("サーチエンジン") | |
| traysearch.add(trayyahoo) | |
| traysearch.add(excite) | |
| traysearch.add(google) | |
| page = factory.create_page("LinkPage", "結城 浩") | |
| page.add(traynews) | |
| page.add(traysearch) | |
| page.output | |
| end | |
| main(ARGV) |
| #/usr/bin/ruby -Ku | |
| require './string-width' | |
| class Display | |
| def initialize(impl) | |
| @impl = impl | |
| end | |
| def open | |
| @impl.raw_open | |
| end | |
| def print | |
| @impl.raw_print | |
| end | |
| def close | |
| @impl.raw_close | |
| end | |
| def display | |
| open | |
| close | |
| end | |
| end | |
| class CountDisplay < Display | |
| def initialize(impl) | |
| super | |
| end | |
| def multi_display(times) | |
| open | |
| times.times do | |
| end | |
| close | |
| end | |
| end | |
| class DisplayImpl | |
| def raw_open | |
| # pass | |
| end | |
| def raw_print | |
| # pass | |
| end | |
| def raw_close | |
| # pass | |
| end | |
| end | |
| class StringDisplayImpl < DisplayImpl | |
| def initialize(string) | |
| @string = string | |
| @width = string.width | |
| end | |
| def raw_open | |
| print_line | |
| end | |
| def raw_print | |
| puts "|#@string|" | |
| end | |
| def raw_close | |
| print_line | |
| end | |
| def print_line | |
| puts "+#{'-' * @width}+" | |
| end | |
| end | |
| def main | |
| d1 = Display.new(StringDisplayImpl.new("Hello, Japan.")) | |
| d2 = CountDisplay.new(StringDisplayImpl.new("Hello, world.")) | |
| d3 = CountDisplay.new(StringDisplayImpl.new("Hello, Universe.")) | |
| d1.display | |
| d2.display | |
| d3.display | |
| d3.multi_display(5) | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Hand | |
| HANDVALUE_GUU = 0 | |
| HANDVALUE_CHO = 1 | |
| HANDVALUE_PAA = 2 | |
| attr :handvalue | |
| def initialize(handvalue) | |
| @handvalue = handvalue | |
| end | |
| @@hand = [ | |
| Hand.new(HANDVALUE_GUU), | |
| Hand.new(HANDVALUE_CHO), | |
| Hand.new(HANDVALUE_PAA) | |
| ] | |
| @@name = %w{グー チョキ パー} | |
| def self.get_hand(handvalue) | |
| return @@hand[handvalue] | |
| end | |
| def stronger_than?(h) | |
| return fight(h) == 1 | |
| end | |
| def weaker_than?(h) | |
| return fight(h) == -1 | |
| end | |
| def fight(h) | |
| if self == h | |
| return 0 | |
| elsif (@handvalue + 1) % 3 == h.handvalue | |
| return 1 | |
| else | |
| return -1 | |
| end | |
| end | |
| def to_s | |
| return @@name[@handvalue] | |
| end | |
| end | |
| class Strategy | |
| def next_hand | |
| # pass | |
| end | |
| def study(win) | |
| # pass | |
| end | |
| end | |
| class WinningStrategy | |
| def initialize(seed) | |
| srand(seed) | |
| @won = false | |
| end | |
| def next_hand | |
| unless @won | |
| @prev_hand = Hand.get_hand(rand(3)) | |
| end | |
| return @prev_hand | |
| end | |
| def study(win) | |
| @won = win | |
| end | |
| end | |
| class ProbStrategy | |
| def initialize(seed) | |
| srand(seed) | |
| @prev_hand_value = 0 | |
| @current_hand_value = 0 | |
| @history = [ | |
| [1, 1, 1], | |
| [1, 1, 1], | |
| [1, 1, 1] | |
| ] | |
| end | |
| def next_hand | |
| bet = rand(get_sum(@current_hand_value)) | |
| handvalue = 0 | |
| if bet < @history[@current_hand_value][0] | |
| handvalue = 0 | |
| elsif bet < @history[@current_hand_value][0] + @history[@current_hand_value][1] | |
| handvalue = 1 | |
| else | |
| handvalue = 2 | |
| end | |
| @prev_hand_value = @current_hand_value | |
| @current_hand_value = handvalue | |
| return Hand.get_hand(handvalue) | |
| end | |
| def get_sum(hv) | |
| sum = 0 | |
| 3.times do |i| | |
| sum += @history[hv][i] | |
| end | |
| return sum | |
| end | |
| def study(win) | |
| if win | |
| @history[@prev_hand_value][@current_hand_value] += 1 | |
| else | |
| @history[@prev_hand_value][(@current_hand_value + 1) % 3] += 1 | |
| @history[@prev_hand_value][(@current_hand_value + 2) % 3] += 1 | |
| end | |
| end | |
| end | |
| class Player | |
| def initialize(name, strategy) | |
| @name = name | |
| @strategy = strategy | |
| @wincount = 0 | |
| @losecount = 0 | |
| @gamecount = 0 | |
| end | |
| def next_hand | |
| return @strategy.next_hand | |
| end | |
| def win | |
| @strategy.study(true) | |
| @wincount += 1 | |
| @gamecount += 1 | |
| end | |
| def lose | |
| @strategy.study(false) | |
| @losecount += 1 | |
| @gamecount += 1 | |
| end | |
| def even | |
| @gamecount += 1 | |
| end | |
| def to_s | |
| return "[#@name:#@gamecount games, #@wincount win, #@losecount lose]" | |
| end | |
| end | |
| def main(args) | |
| if args.length != 2 | |
| puts "Usage: ruby #$0 randomseed1 randomseed2" | |
| puts "Example: ruby #$0 314 15" | |
| exit(0) | |
| end | |
| seed1 = args[0].to_i | |
| seed2 = args[1].to_i | |
| player1 = Player.new("Taro", WinningStrategy.new(seed1)) | |
| player2 = Player.new("Hana", ProbStrategy.new(seed2)) | |
| 10000.times do | |
| next_hand1 = player1.next_hand | |
| next_hand2 = player2.next_hand | |
| if next_hand1.stronger_than?(next_hand2) | |
| puts "Winner:#{player1}" | |
| player1.win | |
| player2.lose | |
| elsif next_hand2.stronger_than?(next_hand1) | |
| puts "Winner:#{player2}" | |
| player1.lose | |
| player2.win | |
| else | |
| puts "Even..." | |
| player1.even | |
| player2.even | |
| end | |
| end | |
| puts "Total result:" | |
| puts player1 | |
| puts player2 | |
| end | |
| main(ARGV) |
| #!/usr/bin/ruby -Ku | |
| class Entry | |
| def get_name | |
| # pass | |
| end | |
| def get_size | |
| # pass | |
| end | |
| def add(entry) | |
| raise FileTreatmentException.new | |
| end | |
| def print_list(prefix = "") | |
| # pass | |
| end | |
| def to_s | |
| return get_name + " (#{get_size})" | |
| end | |
| end | |
| class FileEntry < Entry | |
| def initialize(name, size) | |
| @name = name | |
| @size = size | |
| end | |
| def get_name | |
| return @name | |
| end | |
| def get_size | |
| return @size | |
| end | |
| def print_list(prefix = "") | |
| puts "#{prefix}/#{self}" | |
| end | |
| end | |
| class Directory < Entry | |
| def initialize(name) | |
| @directory = [] | |
| @name = name | |
| end | |
| def get_name | |
| return @name | |
| end | |
| def get_size | |
| size = 0 | |
| @directory.each do |entry| | |
| size += entry.get_size | |
| end | |
| return size | |
| end | |
| def add(entry) | |
| @directory.push(entry) | |
| return self | |
| end | |
| def print_list(prefix = "") | |
| puts "#{prefix}/#{self}" | |
| @directory.each do |entry| | |
| entry.print_list("#{prefix}/#@name") | |
| end | |
| end | |
| end | |
| class FileTreatmentException < Exception | |
| def initialize(msg = "") | |
| super | |
| end | |
| end | |
| def main | |
| begin | |
| puts "Making root entries..." | |
| rootdir = Directory.new("root") | |
| bindir = Directory.new("bin") | |
| tmpdir = Directory.new("tmp") | |
| usrdir = Directory.new("usr") | |
| rootdir.add(bindir) | |
| rootdir.add(tmpdir) | |
| rootdir.add(usrdir) | |
| bindir.add(FileEntry.new("vi", 10000)) | |
| bindir.add(FileEntry.new("latex", 20000)) | |
| rootdir.print_list | |
| puts "" | |
| puts "Making user entries..." | |
| yuki = Directory.new("yuki") | |
| hanako = Directory.new("hanako") | |
| tomura = Directory.new("tomura") | |
| usrdir.add(yuki) | |
| usrdir.add(hanako) | |
| usrdir.add(tomura) | |
| yuki.add(FileEntry.new("diary.html", 100)) | |
| yuki.add(FileEntry.new("Composite.java", 200)) | |
| hanako.add(FileEntry.new("memo.tex", 300)) | |
| tomura.add(FileEntry.new("game.doc", 400)) | |
| tomura.add(FileEntry.new("junk.mail", 500)) | |
| rootdir.print_list | |
| rescue => e | |
| p e | |
| end | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| require './string-width' | |
| class Display | |
| def get_columns | |
| # pass | |
| end | |
| def get_rows | |
| # pass | |
| end | |
| def get_row_text(row) | |
| # pass | |
| end | |
| def show | |
| get_rows.times do |i| | |
| puts get_row_text(i) | |
| end | |
| end | |
| end | |
| class StringDisplay < Display | |
| def initialize(string) | |
| @string = string | |
| end | |
| def get_columns | |
| return @string.width | |
| end | |
| def get_rows | |
| return 1 | |
| end | |
| def get_row_text(row) | |
| if row == 0 | |
| return @string | |
| else | |
| return nil | |
| end | |
| end | |
| end | |
| class Border < Display | |
| def initialize(display) | |
| @display = display | |
| end | |
| end | |
| class SideBorder < Border | |
| def initialize(display, ch) | |
| super(display) | |
| @border_char = ch | |
| end | |
| def get_columns | |
| return 1 + @display.get_columns + 1 | |
| end | |
| def get_rows | |
| return @display.get_rows | |
| end | |
| def get_row_text(row) | |
| return "#@border_char#{@display.get_row_text(row)}#@border_char" | |
| end | |
| end | |
| class FullBorder < Border | |
| def initialize(display) | |
| super | |
| end | |
| def get_columns | |
| return 1 + @display.get_columns + 1 | |
| end | |
| def get_rows | |
| return 1 + @display.get_rows + 1 | |
| end | |
| def get_row_text(row) | |
| if row == 0 | |
| return "+#{make_line('-', @display.get_columns)}+" | |
| elsif row == @display.get_rows + 1 | |
| return "+#{make_line('-', @display.get_columns)}+" | |
| else | |
| return "|#{@display.get_row_text(row - 1)}|" | |
| end | |
| end | |
| def make_line(ch, count) | |
| return ch * count | |
| end | |
| end | |
| def main | |
| b1 = StringDisplay.new("Hello, world.") | |
| b2 = SideBorder.new(b1, "#") | |
| b3 = FullBorder.new(b2) | |
| b1.show | |
| b2.show | |
| b3.show | |
| b4 = ( | |
| SideBorder.new( | |
| FullBorder.new( | |
| FullBorder.new( | |
| SideBorder.new( | |
| FullBorder.new( | |
| StringDisplay.new("こんにちは。") | |
| ), | |
| "*" | |
| ) | |
| ) | |
| ), | |
| "/" | |
| ) | |
| ) | |
| b4.show | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Visitor | |
| def visit(entry) | |
| # pass | |
| end | |
| end | |
| class Element | |
| def accept(v) | |
| # pass | |
| end | |
| end | |
| class Entry < Element | |
| def get_name | |
| # pass | |
| end | |
| def get_size | |
| # pass | |
| end | |
| def add(entry) | |
| raise FileTreatmentException.new | |
| end | |
| def to_s | |
| return "#{get_name} (#{get_size})" | |
| end | |
| end | |
| class FileEntry < Entry | |
| def initialize(name, size) | |
| @name = name | |
| @size = size | |
| end | |
| def get_name | |
| return @name | |
| end | |
| def get_size | |
| return @size | |
| end | |
| def accept(v) | |
| v.visit(self) | |
| end | |
| end | |
| class Directory < Entry | |
| def initialize(name) | |
| @name = name | |
| @dir = [] | |
| end | |
| def get_name | |
| return @name | |
| end | |
| def get_size | |
| size = 0 | |
| @dir.each do |entry| | |
| size += entry.get_size | |
| end | |
| return size | |
| end | |
| def add(entry) | |
| @dir.push(entry) | |
| return self | |
| end | |
| def each | |
| @dir.each do |d| | |
| yield(d) | |
| end | |
| end | |
| def accept(v) | |
| v.visit(self) | |
| end | |
| end | |
| class ListVisitor < Visitor | |
| @currentdir = "" | |
| def visit_file(file) | |
| puts "#@currentdir/#{file}" | |
| end | |
| def visit_directory(directory) | |
| puts "#@currentdir/#{directory}" | |
| savedir = @currentdir | |
| @currentdir = "#@currentdir/#{directory.get_name}" | |
| directory.each do |entry| | |
| entry.accept(self) | |
| end | |
| end | |
| def visit(entry) | |
| if entry.instance_of?(FileEntry) | |
| visit_file(entry) | |
| elsif entry.instance_of?(Directory) | |
| visit_directory(entry) | |
| else | |
| p entry.class | |
| end | |
| end | |
| end | |
| class FileTreatmentException < Exception | |
| def initialize(msg = "") | |
| super | |
| end | |
| end | |
| def main | |
| begin | |
| puts "Making root entries..." | |
| rootdir = Directory.new("root") | |
| bindir = Directory.new("bin") | |
| tmpdir = Directory.new("tmp") | |
| usrdir = Directory.new("usr") | |
| rootdir.add(bindir) | |
| rootdir.add(tmpdir) | |
| rootdir.add(usrdir) | |
| bindir.add(FileEntry.new("vi", 10000)) | |
| bindir.add(FileEntry.new("latex", 20000)) | |
| rootdir.accept(ListVisitor.new) | |
| puts "" | |
| puts "Making user entries..." | |
| yuki = Directory.new("yuki") | |
| hanako = Directory.new("hanako") | |
| tomura = Directory.new("tomura") | |
| usrdir.add(yuki) | |
| usrdir.add(hanako) | |
| usrdir.add(tomura) | |
| yuki.add(FileEntry.new("diary.html", 100)) | |
| yuki.add(FileEntry.new("Composite.java", 200)) | |
| hanako.add(FileEntry.new("memo.tex", 300)) | |
| tomura.add(FileEntry.new("game.doc", 400)) | |
| tomura.add(FileEntry.new("junk.mail", 500)) | |
| rootdir.accept(ListVisitor.new) | |
| rescue => e | |
| p e | |
| end | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Trouble | |
| def initialize(number) | |
| @number = number | |
| end | |
| def get_number | |
| return @number | |
| end | |
| def to_s | |
| return "[Trouble #@number]" | |
| end | |
| end | |
| class Support | |
| def initialize(name) | |
| @name = name | |
| end | |
| def set_next(next_support) | |
| @next = next_support | |
| return next_support | |
| end | |
| def support(trouble) | |
| if resolve(trouble) | |
| done(trouble) | |
| elsif @next != nil | |
| @next.support(trouble) | |
| else | |
| fail(trouble) | |
| end | |
| end | |
| def to_s | |
| return "[#@name]" | |
| end | |
| def resolve(trouble) | |
| # pass | |
| end | |
| def done(trouble) | |
| puts "#{trouble} is resolved by #{self}." | |
| end | |
| def fail(trouble) | |
| puts "#{trouble} cannot be resolved." | |
| end | |
| end | |
| class NoSupport < Support | |
| def initialize(name) | |
| super | |
| end | |
| def resolve(trouble) | |
| return false | |
| end | |
| end | |
| class LimitSupport < Support | |
| def initialize(name, limit) | |
| super(name) | |
| @limit = limit | |
| end | |
| def resolve(trouble) | |
| if trouble.get_number < @limit | |
| return true | |
| else | |
| return false | |
| end | |
| end | |
| end | |
| class OddSupport < Support | |
| def initialize(name) | |
| super | |
| end | |
| def resolve(trouble) | |
| if trouble.get_number % 2 == 1 | |
| return true | |
| else | |
| return false | |
| end | |
| end | |
| end | |
| class SpecialSupport < Support | |
| def initialize(name, number) | |
| super(name) | |
| @number = number | |
| end | |
| def resolve(trouble) | |
| if trouble.get_number == @number | |
| return true | |
| else | |
| return false | |
| end | |
| end | |
| end | |
| def main | |
| alice = NoSupport.new("Alice") | |
| bob = LimitSupport.new("Bob", 100) | |
| charlie = SpecialSupport.new("Charlie", 429) | |
| diana = LimitSupport.new("Diana", 200) | |
| elmo = OddSupport.new("Elmo") | |
| fred = LimitSupport.new("Fred", 300) | |
| alice.set_next(bob).set_next(charlie).set_next(diana).set_next(elmo).set_next(fred) | |
| 0.step(500, 33) do |i| | |
| alice.support(Trouble.new(i)) | |
| end | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Properties | |
| def initialize | |
| @hash = {} | |
| end | |
| def load(filename) | |
| IO.foreach(filename) do |line| | |
| @hash.store(*line.chomp.split("=", 2)) | |
| end | |
| end | |
| def get_property(key) | |
| return @hash[key] | |
| end | |
| def show | |
| @hash.each do |k, v| | |
| p "#{k} #{v}" | |
| end | |
| end | |
| end | |
| class Database | |
| private_class_method :new | |
| def self.get_properties(dbname) | |
| filename = "#{dbname}.txt" | |
| prop = Properties.new | |
| begin | |
| prop.load(filename) | |
| rescue => e | |
| puts "Warning: #{filename} is not found." | |
| p e | |
| end | |
| return prop | |
| end | |
| end | |
| class HTMLWriter | |
| def initialize(writer) | |
| @writer = writer | |
| end | |
| def title(title) | |
| @writer.write("<html>") | |
| @writer.write("<head>") | |
| @writer.write("<title>#{title}</title>") | |
| @writer.write("</head>") | |
| @writer.write("<body>\n") | |
| @writer.write("<h1>#{title}</h1>\n") | |
| end | |
| def paragraph(msg) | |
| @writer.write("<p>#{msg}</p>\n") | |
| end | |
| def link(href, caption) | |
| paragraph(%{<a href="#{href}">#{caption}</a>}) | |
| end | |
| def mailto(mailaddr, username) | |
| link("mailto:#{mailaddr}", username) | |
| end | |
| def close | |
| @writer.write("</body>") | |
| @writer.write("</html>\n") | |
| @writer.close | |
| end | |
| end | |
| class PageMaker | |
| private_class_method :new | |
| def self.make_welcome_page(mailaddr, filename) | |
| begin | |
| mailprop = Database.get_properties("maildata") | |
| username = mailprop.get_property(mailaddr) | |
| writer = HTMLWriter.new(open(filename, "w")) | |
| writer.title("Welcome to #{username}'s page!") | |
| writer.paragraph("#{username}のページへようこそ。") | |
| writer.paragraph("メールまっていますね。") | |
| writer.mailto(mailaddr, username) | |
| writer.close | |
| puts "#{filename} is created for #{mailaddr} (#{username})" | |
| rescue => e | |
| p e | |
| end | |
| end | |
| end | |
| def main | |
| PageMaker.make_welcome_page("[email protected]", "welcome.html") | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| #!/usr/bin/ruby -Ku | |
| class Observer | |
| def update(generator) | |
| # pass | |
| end | |
| end | |
| class NumberGenerator | |
| def initialize | |
| @observers = [] | |
| end | |
| def add_observer(observer) | |
| @observers.push(observer) | |
| end | |
| def delete_observer(observer) | |
| @observers.delete(observer) | |
| end | |
| def notify_observers | |
| @observers.each do |o| | |
| o.update(self) | |
| end | |
| end | |
| def get_number | |
| # pass | |
| end | |
| def execute | |
| # pass | |
| end | |
| end | |
| class RandomNumberGenerator < NumberGenerator | |
| def get_number | |
| return @number | |
| end | |
| def execute | |
| 20.times do | |
| @number = rand(50) | |
| notify_observers | |
| end | |
| end | |
| end | |
| class DigitObserver | |
| def update(generator) | |
| puts "DigitObserver:#{generator.get_number}" | |
| begin | |
| sleep 0.1 | |
| rescue => e | |
| # pass | |
| end | |
| end | |
| end | |
| class GraphObserver | |
| def update(generator) | |
| puts "GraphObserver:#{'*' * generator.get_number}" | |
| begin | |
| sleep 0.1 | |
| rescue => e | |
| # pass | |
| end | |
| end | |
| end | |
| def main | |
| generator = RandomNumberGenerator.new | |
| observer1 = DigitObserver.new | |
| observer2 = GraphObserver.new | |
| generator.add_observer(observer1) | |
| generator.add_observer(observer2) | |
| generator.execute | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class Memento | |
| def initialize(money) | |
| @money = money | |
| @fruits = [] | |
| end | |
| def get_money | |
| return @money | |
| end | |
| def add_fruit(fruit) | |
| @fruits.push(fruit) | |
| end | |
| def get_fruits | |
| return @fruits | |
| end | |
| end | |
| class Gamer | |
| def initialize(money) | |
| @money = money | |
| @fruits = [] | |
| @fruitsname = %w{リンゴ ぶどう バナナ みかん} | |
| end | |
| def get_money | |
| return @money | |
| end | |
| def bet | |
| dice = rand(6) + 1 | |
| if dice == 1 | |
| @money += 100 | |
| puts "所持金が増えました。" | |
| elsif dice == 2 | |
| @money /= 2 | |
| puts "所持金が半分になりました。" | |
| elsif dice == 6 | |
| f = get_fruit | |
| puts "フルーツ(#{f})をもらいました。" | |
| @fruits.push(f) | |
| else | |
| puts "何も起こりませんでした。" | |
| end | |
| end | |
| def create_memento | |
| m = Memento.new(@money) | |
| @fruits.each do |f| | |
| if f.match(/^おいしい/) | |
| m.add_fruit(f) | |
| end | |
| end | |
| return m | |
| end | |
| def restore_memento(memento) | |
| @money = memento.get_money | |
| @fruits = memento.get_fruits | |
| end | |
| def to_s | |
| return "[money = #@money, fruits = [#{@fruits.join(', ')}]]" | |
| end | |
| def get_fruit | |
| prefix = (rand(2) == 1) ? "おいしい" : "" | |
| return "#{prefix}#{@fruitsname[rand(@fruitsname.length)]}" | |
| end | |
| end | |
| def main | |
| gamer = Gamer.new(100) | |
| memento = gamer.create_memento | |
| 100.times do |i| | |
| puts "==== #{i}" | |
| puts "現状:#{gamer}" | |
| gamer.bet | |
| puts "所持金は#{gamer.get_money}円になりました。" | |
| if gamer.get_money > memento.get_money | |
| puts " (だいぶ増えたので、現在の状態を保存しておこう)" | |
| memento = gamer.create_memento | |
| elsif gamer.get_money < memento.get_money / 2 | |
| puts " (だいぶ減ったので、以前の状態に復帰しよう)" | |
| gamer.restore_memento(memento) | |
| end | |
| begin | |
| sleep 1 | |
| rescue => e | |
| # pass | |
| end | |
| puts "" | |
| end | |
| end | |
| main |
| #!/usr/bin/ruby -Ku | |
| class BigChar | |
| def initialize(charname) | |
| @charname = charname | |
| begin | |
| @fontdata = IO.read("big#{charname}.txt") | |
| rescue => e | |
| @fontdata = "#{charname}?" | |
| end | |
| end | |
| def print | |
| puts @fontdata | |
| end | |
| end | |
| class BigCharFactory | |
| @@singleton = new | |
| @@pool = {} | |
| def self.get_instance | |
| return @@singleton | |
| end | |
| def get_big_char(charname) | |
| bc = @@pool[charname] | |
| if bc == nil | |
| bc = BigChar.new(charname) | |
| @@pool[charname] = bc | |
| end | |
| return bc | |
| end | |
| private_class_method :new | |
| end | |
| class BigString | |
| def initialize(string) | |
| @bigchars = Array.new(string.length) | |
| factory = BigCharFactory.get_instance | |
| @bigchars.length.times do |i| | |
| @bigchars[i] = factory.get_big_char(string[i,1]) | |
| end | |
| end | |
| def print | |
| @bigchars.length.times do |i| | |
| @bigchars[i].print | |
| end | |
| end | |
| end | |
| def main(args) | |
| if args.length == 0 | |
| puts "Usage: ruby #$0 digits" | |
| puts "Example: ruby #$0 1212123" | |
| exit(0) | |
| end | |
| bs = BigString.new(args[0]) | |
| bs.print | |
| end | |
| main(ARGV) |
| #!/usr/bin/ruby -Ku | |
| class Printer | |
| def initialize(name = "") | |
| if name == "" | |
| heavy_job("Printerのインスタンスを生成中") | |
| else | |
| @name = name | |
| heavy_job("Printerのインスタンス(#{name})を生成中") | |
| end | |
| end | |
| def set_printer_name(name) | |
| @name = name | |
| end | |
| def get_printer_name | |
| return @name | |
| end | |
| def p(string) | |
| puts "=== #@name ===" | |
| puts string | |
| end | |
| def heavy_job(msg) | |
| STDOUT.sync = true | |
| print msg | |
| 5.times do | |
| begin | |
| sleep(1) | |
| rescue => e | |
| # pass | |
| end | |
| print "." | |
| end | |
| puts "完了。" | |
| end | |
| end | |
| class Printable | |
| def set_print_name(name) | |
| # pass | |
| end | |
| def get_print_name | |
| # pass | |
| end | |
| def p(string) | |
| # pass | |
| end | |
| end | |
| class PrinterProxy | |
| def initialize(name = "") | |
| @name = name | |
| end | |
| def set_printer_name(name) | |
| if @real != nil | |
| @real.set_printer_name(name) | |
| end | |
| @name = name | |
| end | |
| def get_printer_name | |
| return @name | |
| end | |
| def p(string) | |
| realize | |
| @real.p(string) | |
| end | |
| def realize | |
| if @real == nil | |
| @real = Printer.new(@name) | |
| end | |
| end | |
| end | |
| def main | |
| pr = PrinterProxy.new("Alice") | |
| puts "名前は現在#{pr.get_printer_name}です。" | |
| pr.set_printer_name("Bob") | |
| puts "名前は現在#{pr.get_printer_name}です。" | |
| pr.p("Hello, world.") | |
| end | |
| main |
| ................ | |
| ................ | |
| ................ | |
| ................ | |
| ..##########.... | |
| ................ | |
| ................ | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..##......##.... | |
| ..##......##.... | |
| ..##......##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| ......##........ | |
| ..######........ | |
| ......##........ | |
| ......##........ | |
| ......##........ | |
| ......##........ | |
| ..##########.... | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..........##.... | |
| ......####...... | |
| ....##.......... | |
| ..##............ | |
| ..##########.... | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..........##.... | |
| ......####...... | |
| ..........##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| ........##...... | |
| ......####...... | |
| ....##..##...... | |
| ..##....##...... | |
| ..##########.... | |
| ........##...... | |
| ......######.... | |
| ................ |
| ..##########.... | |
| ..##............ | |
| ..##............ | |
| ..########...... | |
| ..........##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..##............ | |
| ..########...... | |
| ..##......##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| ..##########.... | |
| ..##......##.... | |
| ..........##.... | |
| ........##...... | |
| ......##........ | |
| ......##........ | |
| ......##........ | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..##......##.... | |
| ....######...... | |
| ..##......##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| ....######...... | |
| ..##......##.... | |
| ..##......##.... | |
| ....########.... | |
| ..........##.... | |
| ..##......##.... | |
| ....######...... | |
| ................ |
| [email protected]=Hiroshi Yuki | |
| [email protected]=Hanako Sato | |
| [email protected]=Tomura | |
| [email protected]=Mamoru Takahashi |
| #!/usr/bin/ruby -Ku | |
| # 文字列の表示幅(半角何個分)を返す. | |
| # 暗に全角文字 = 3バイト と仮定している. | |
| # 厳密には http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt | |
| # を参照した実装を行う必要がある | |
| class String | |
| def width | |
| b = self.size # n * 1 + w * 3 | |
| l = self.split(//u).length # n * 1 + w * 1 | |
| return (b + l) / 2 # n * 1 + w * 2 | |
| end | |
| end | |
| if __FILE__ == $0 | |
| def demo(string) | |
| width = string.width | |
| puts %{string #=> "#{string}"} | |
| puts "string.width #=> #{width}" | |
| puts "|#{string}|" | |
| puts "|#{'-' * width}| #{width}" | |
| puts "" | |
| end | |
| demo("Hello, world.") | |
| demo("増補改訂版 Java言語で学ぶデザインパターン入門 (結城 浩)") | |
| end |