Observer
概要
オブジェクトの監視を行う
オブジェクトに変化が発生した時、他のオブジェクトに通知
パラメータを変更した事が他の部分に知れ渡ることが必要なケース
Subject:変化するオブジェクト
Observer:変化を通知する
ConcreteObserver:変化に関連して具体的な処理を行う
メリット
オブジェクト間の依存度を下げる
サブジェクトは通知を意識しなくていい
例
require 'observer'
class Player
include Observable
attr_accessor :times, :level
attr_reader :name, :item
def initialize(name, times, level)
@name = name
@times = times
@level = level
@item = ["Soccer", "Baseball", "Basketball"]
# 通知するオブジェクトを追加
add_observer(LevelUp.new)
add_observer(Play.new)
end
def level=(new_level)
@level = new_level
# 通知
changed
notify_observers(self)
end
end
# ConcreteObserver 1
class LevelUp
def update(obj)
obj.times += 1
puts "#{obj.times}回目 #{obj.name} : Level => #{obj.level}"
end
end
# ConcreteObserver 2
class Play
def update(obj)
puts "Level #{obj.level} => He can play #{obj.item[obj.times]}"
end
end
john = Player.new('John', 0, 10)
p "--- Level Up ---"
john.level = 11
p "--- Level Up ---"
john.level = 12
Strategy
概要
処理をオブジェクトとして切り出す(委譲する)
処理の切替を容易にする
例
Reportから見て、HTMLFormatterとPlaneTextFormatterが同じように使える
# Strategy
class Formatter
def output_report(title, text)
raise 'Called abstract method !!'
end
end
# ConcreteStrategy 1
class HTMLFormatter < Formatter
@@count = 0
def output_report(report)
@@count += 1
puts "<html><head><title>#{report.title}</title></head><body>"
puts "<h1>#{report.body}</h1>"
puts "<h1>#{@@count}回目</h1>"
report.text.each { |line| puts "<p>#{line}</p>" }
puts '</body></html>'
end
end
# ConcreteStrategy 2
class PlaneTextFormatter < Formatter
@@count = 0
def output_report(report)
@@count += 1
puts "***** #{report.title} *****"
puts "* #{report.body} *"
puts "#{@@count}回目"
report.text.each { |line| puts(line) }
end
end
# Report.formatterにHTMLかTextかを保持する
# Report.formatter.output_report(self)
class Report
attr_reader :title, :text, :body
attr_accessor :formatter
def initialize(formatter)
@title = 'Report Title'
@text = %w(text1)
@body = 'Hello World!'
@formatter = formatter
end
def output_report
@formatter.output_report(self)
end
end
report = Report.new(HTMLFormatter.new)
report.output_report
# formatterを変更するだけ
report.formatter = PlaneTextFormatter.new
report.output_report
# formatterを変更するだけ
report.formatter = HTMLFormatter.new
report.output_report
# <html><head><title>Report Title</title></head><body>
# <h1>Hello World!</h1>
# <h1>1回目</h1>
# <p>text1</p>
# </body></html>
# ***** Report Title *****
# * Hello World! *
# 1回目
# text1
# <html><head><title>Report Title</title></head><body>
# <h1>Hello World!</h1>
# <h1>2回目</h1>
# <p>text1</p>
# </body></html>
# ブロックを利用する
class Report
attr_reader :title, :text
attr_accessor :formatter, :count
def initialize(&formatter)
@title = 'report title'
@text = %w(text1 text2)
@formatter = formatter
@count = 0
end
def output_report
@count += 1
@formatter.call(self)
end
end
HTML_FORMATTER = lambda do |context|
puts "<html><head><title>#{context.title}</title></head><body>"
puts "<h1>#{context.count}回目</h1>"
context.text.each { |line| puts "<p>#{line}</p>" }
puts '</body></html>'
end
PLANE_TEXT_FORMATTER = lambda do |context|
puts "***** #{context.title} *****"
puts "#{context.count}回目"
context.text.each { |line| puts(line) }
end
report = Report.new(&HTML_FORMATTER)
report.output_report
report.formatter = PLANE_TEXT_FORMATTER
report.output_report
# <html><head><title>report title</title></head><body>
# <h1>1回目</h1>
# <p>text1</p>
# <p>text2</p>
# </body></html>
# ***** report title *****
# 2回目
# text1
# text2
Command
概要
処理をクラスとして切り出し、変わるところと変わらないところを分離する
ベースのCommandクラスは不変部分であり、そこに可変のコマンドオブジェクトを実装する
例
class Command
attr_reader :description
def initialize(description)
@description = description
end
def execute
end
def undo_execute
end
end
require "fileutils"
class CreateFile < Command
def initialize(path, contents)
super("Create file : #{path}")
@path = path
@contents = contents
end
def execute
f = File.open(@path, "w")
f.write(@contents)
f.close
end
def undo_execute
File.delete(@path)
end
end
class DeleteFile < Command
def initialize(path)
super("Delete file : #{path}")
@path = path
end
def execute
if File.exists?(@path)
@content = File.read(@path)
end
File.delete(@path)
end
def undo_execute
f = File.open(@path, "w")
f.write(@contents)
f.close
end
end
class CopyFile < Command
def initialize(source, target)
super("Copy file : #{source} to #{target}")
@source = source
@target = target
end
def execute
FileUtils.copy(@source, @target)
end
def undo_execute
File.delete(@target)
if(@contents)
f = File.open(@target, "w")
f.write(@contents)
f.close
end
end
end
class CompositeCommand < Command
def initialize
@commands = []
end
def add_command(cmd)
@commands << cmd
end
def execute
@commands.each { |cmd| cmd.execute }
end
def undo_execute
@commands.reverse.each { |cmd| cmd.undo_execute }
end
def description
description = ""
@commands.each { |cmd| description += cmd.description + "\n"}
description
end
end
command_list = CompositeCommand.new
command_list.add_command(CreateFile.new("file1.txt", "hello world\n"))
command_list.add_command(CopyFile.new("file1.txt", "file2.txt"))
command_list.add_command(DeleteFile.new("file1.txt"))
command_list.execute
# <CreateFile:0x007fc9b1d13e18 @contents="hello world\n", @description="Create file : file1.txt", @path="file1.txt">,
# <CopyFile:0x007fc9b50f3800 @description="Copy file : file1.txt to file2.txt", @source="file1.txt", @target="file2.txt">,
# <DeleteFile:0x007fc9b1d4b868 @content="hello world\n", @description="Delete file : file1.txt", @path="file1.txt">
command_list.undo_execute
# <DeleteFile:0x007fc9b1d4b868 @content="hello world\n", @description="Delete file : file1.txt", @path="file1.txt">,
# <CopyFile:0x007fc9b50f3800 @description="Copy file : file1.txt to file2.txt", @source="file1.txt", @target="file2.txt">,
# <CreateFile:0x007fc9b1d13e18 @contents="hello world\n", @description="Create file : file1.txt", @path="file1.txt">
Template Method
概要
抽象的な処理をベースのクラス側に
サブクラスに変化するロジックを
例
class TemplateMethod
def initialize
end
def hoge
puts hoge1
puts hoge2
puts hoge3
end
def hoge1
end
def hoge2
end
def hoge3
end
end
class Sub1 < TemplateMethod
def hoge1
"--hoge1--"
end
def hoge2
"--hoge2--"
end
def hoge3
"--hoge3--"
end
end
class Sub2 < TemplateMethod
def hoge1
"**hoge1**"
end
def hoge2
"**hoge2**"
end
def hoge3
"**hoge3**"
end
end
template = TemplateMethod.new()
template.hoge
template1 = Sub1.new()
template1.hoge
template2 = Sub2.new()
template2.hoge
Iterator
概要
集合を数え上げる処理を共通化することで、集合を扱いやすくする
例
a.rb
class Iterator
def initialize(array)
@array = array
@index = 0
end
def has_next
return (@index + 1) <= @array.count
end
def next
value = @array[@index]
@index = @index + 1
value
end
end
b.rb
require_relative 'a'
iterator = Iterator.new(%w(hoge huga piyo))
while iterator.has_next
puts(iterator.next)
end
# hoge
# huga
# piyo