Murayama blog.

プログラミング教育なブログ

Adapter

Rubyによるデザインパターン生活。若干飽きてきたけど続けます。
本日はAdapterパターンを取り上げます。


Rubyによるデザインパターン

Rubyによるデザインパターン


Adapterパターンは、インターフェースの不一致を埋めるためのパターンです。
ここでいうインターフェースの不一致というのは、
メソッドのネーミング、シグネチャの不一致のことを意味しています。


例えば、次のようなプログラムがあるとします。
画面に文字列を出力するだけのシンプルなものです。
#今回は本に載ってるプログラムをもろパクリではなく、ちょっとアレンジしています。

class Renderer
  def execute(text_object)
    text_object.print
  end
end

class TextObject
  def print
    puts "TextObject print!"
  end
end

このプログラムは、Rendererクラスのexecuteメソッドの中で、
引数に受け取ったオブジェクトのprintメソッドを呼び出します。
引数のオブジェクトには、TextObjectを渡すことができます。

renderer = Renderer.new
text_object = TextObject.new
renderer.execute(text_object)


実行結果は以下のとおりです。

TextObject print!


次に、Rendererクラスのexecuteメソッドの引数に与えるオブジェクトを、
さきほどのTextObjectからLegacyTextObjectに変更します。
LegacyTextObjectクラスは以下のとおりです。

class LegacyTextObject
  def show_message
    puts "LegacyTextObject show_message!"
  end
end


ここで問題が発生します。
LegacyTextObjectクラスにはprintメソッドが定義されていません。
その代わりに類似したメソッドであるshow_messageが定義されています。
このままの状態で、さきほどと同じようにプログラムを実行するとエラーが発生してしまいます。

renderer = Renderer.new
lto = LegacyTextObject.new
renderer.execute(lto)

実行結果(NoMethodError)

NoMethodError: private method ‘print’ called for #<LegacyTextObject:0x1ce6c>


エラーの原因はLegacyTextObjectクラスにprintメソッドが定義されていないためです。
一番簡単な解決法はLegacyTextObjectクラスのshow_messageメソッドを、
printメソッドに名前を変更することですが、
LegacyTextObjectクラスが既に別のプログラムから利用されている場合は、変更箇所が増えてしまいます。


上記のような問題の解決策となるのがAdapterパターンです。
Adapterパターンでは、インタフェースの不一致を吸収するためのAdapterクラスを作成します。

class LegacyTextObjectAdapter
  def initialize(legacty_text_object)
    @lto = legacty_text_object
  end
  
  def print
    @lto.show_message
  end
end


実行時には、LegacyTextObjectAdapterを使ってLegacyTextObjectをラップします。

renderer = Renderer.new
lto = LegacyTextObject.new
renderer.execute(LegacyTextObjectAdapter.new(lto))


すると上手く動作します。(実行結果です)

LegacyTextObject show_message!


Adapterパターンのクラス図は、以下のようになります。


各クラスの役割は以下のとおりです。

  • Client
    • Targetのメソッドを呼び出すクラス。
  • Target
    • 実行対象となるクラス(インタフェース)。
  • Adapter
    • Targetインタフェースと、Adapteeクラスの差分を埋めるクラス。
  • Adaptee
    • 実際に動作するクラス。


先ほどのプログラムの場合、以下のような構成になります。

  • Client
    • Rendererクラス。
  • Target
    • なし。Rubyの場合はインタフェース不要。*1
  • Adapter
    • LegacyTextObjectAdapterクラス。
  • Adaptee
    • LegacyTextObjectクラス。


以上がAdapterパターンの説明です。
なんですが、この本の面白いところはここからです。


Rubyは動的型付け言語です。
Javaのようにコンパイル時に変数の型をチェックするような仕組みはありません。
そのため、Adapterパターンにおいても、
Ruby特有の、動的型付け言語特有の解決策が存在します。


例えば、実行時にクラスを修正することで、メソッドを追加することができます。
LegacyTextObjectがロード済みの場合、以下のようにクラス自身を再定義することができます。

class LegacyTextObject
  def print
    show_message
  end
end


これにより実行時にprintメソッドが追加されます。
printメソッドの内部では、show_messageメソッドを呼んでいるので、
結果的にshow_messageメソッドが呼び出されることになります。


上記の方法はクラス定義自体に変更が発生します。
LegacyTextObjectクラスのすべてのオブジェクトはprintメソッドを保持することになります。


クラスに直接メソッドを追加するのではなく、
特定のオブジェクトにだけメソッドを追加することもできます。

lto = LegacyTextObject.new
class << lto
  def print
    show_message
  end
end

この方法ではlto変数に格納されているオブジェクトのみがprintメソッドを保持することになります。
Rubyではオブジェクトに固有なメソッドのことを特異メソッド(Singletonメソッド)と呼びます。
特異メソッドは、以下のように実装することもできます。

lto = LegacyTextObject.new
def lto.print
  show_message
end


以上で説明はおしまいです。
Adapterパターンは理解しやすいパターンだと思います。
ただ、僕の場合はこれまでJavaの考え方(静的型付け)で理解していました。
そのため、Rubyのような動的型付けの世界ではいろんな代替手段があるんだなー、と、
改めて感じました。
プログラミングってほんとうにおもしろいですね、みたいな。

*1:ダックタイピングというやつ

Command

Rubyによるデザインパターン生活。継続中。
本日はCommandパターンを取り上げます。


Rubyによるデザインパターン

Rubyによるデザインパターン


CommandパターンのCommand(コマンド)とは命令の意味です。
この本には、

コマンドはある特定の何かをするための命令です。

と定義されています。


あ、今回からクラス図をアップすることにしました。
それではCommandパターンのクラス図です。


Commandパターンはとてもシンプルな構成になっています。
上記のクラス図では、抽象的なCommandクラス(インタフェース)を実装する、
ConcreteCommand1、ConcreteCommand2クラスを示しています。


Commandインタフェースにはexecuteメソッドが定義されています。
このexecuteメソッドが「ある特定の何かをするための命令」になります。


今回のサンプルに登場するクラスです。

  • Command
    • Commandクラス。名前そのままでCommandパターンのCommandを担当します。
  • CreateFile
    • Commandのサブクラス。ファイルを作成するクラス。
  • DeleteFile
    • Commandのサブクラス。ファイルを削除するクラス。
  • CopyFile
    • Commandのサブクラス。ファイルをコピーするクラス。

※CreateFile、DeleteFile、CopyFileクラスは、ConcreteCommand1(2)クラスのように振る舞います。

  • CompositCommand
    • 複数のコマンドを集約するクラス。自身もCommandのサブクラスであり、保持する複数のコマンドをまとめて実行します。


つづいて、ソースコードです。

require "fileutils"

class Command
  attr_reader :description
  def initialize(description)
    @description = description
  end
  
  def execute
  end
end

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
end


class DeleteFile < Command
  def initialize(path)
    super("Delete file : #{path}")
    @path = path
  end
  
  def execute
    File.delete(@path)
  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
end

class CompositCommand < Command
  def initialize
    @commands = []
  end
  
  def add_command(cmd)
    @commands << cmd
  end
  
  def execute
    @commands.each { |cmd| cmd.execute }
  end
  
  def description
    description = ""
    @commands.each { |cmd| description += cmd.description + "\n"}
    description
  end
end


cmds = CompositCommand.new
cmds.add_command(CreateFile.new("file1.txt", "hello world\n"))
cmds.add_command(CopyFile.new("file1.txt", "file2.txt"))
cmds.add_command(DeleteFile.new("file1.txt"))

cmds.execute

Commandクラスとそのサブクラスを定義したあと、CompositCommandオブジェクトを生成し、
CreateFile、CopyFile、DeleteFileオブジェクトを追加しています。
その後、追加したCommandオブジェクトを追加した順にまとめて実行しています。


実行結果は、カレントディレクトリに作成したfile1.txtがコピーされてfile2.txtとなります。
また、コピー元となったfile1.txtは削除されます。
結果としてfile2.txtのみが残ります。


また、さきほどのプログラムの最後で、

puts cmds.description

と実行すると、画面に集約したコマンドの一覧を出力します。

Create file : file1.txt
Copy file : file1.txt to file2.txt
Delete file : file1.txt


ここまでの流れをみると、
一つひとつのCommandの処理はexecuteメソッドを実装するだけで単純なものです。
「executeメソッドを実装した」から「Commandパターンだ」と思っていてはいけません。
本に載っている大事な部分を引用します。

Commandパターンのポイントは、何を行うかの決定と、それの実行を分離することです。
このパターンを使う場合、「これを行え」と命令する代わりに、「これを行う方法を記録しろ」と命令し、その後「記録したことを行え」と命令します。

Commandパターンは命令と実行の分離と考えると良いと思います。


また、Commandパターンの応用編として、
Undo(やりなおし)の実装も紹介されています。
さきほどのプログラムの各Commandクラスにunexecuteメソッドを追加してみます。

require "fileutils"

class Command
  attr_reader :description
  def initialize(description)
    @description = description
  end
  
  def execute
  end
end

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 unexecute
    File.delete(@path)
  end
end

class DeleteFile < Command
  def initialize(path)
    super("Delete file : #{path}")
    @path = path
  end
  
  def execute
    if(File.exists?(@path))
      @contents = File.read(@path)
    end
    File.delete(@path)
  end
  
  def unexecute
    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
    if(File.exists?(@target))
      @contents = File.read(@target)
    end
    FileUtils.copy(@source, @target)
  end
  
  def unexecute
    File.delete(@target)
    if(@contents)
      f = File.open(@target, "w")
      f.write(@contents)
      f.close
    end
  end
end

class CompositCommand < Command
  def initialize
    @commands = []
  end
  
  def add_command(cmd)
    @commands << cmd
  end
  
  def execute
    @commands.each { |cmd| cmd.execute }
  end
  
  def unexecute
    @commands.reverse.each { |cmd| cmd.unexecute }
  end
  
  def description
    description = ""
    @commands.each { |cmd| description += cmd.description + "\n"}
    description
  end
end

cmds = CompositCommand.new

cmds.add_command(CreateFile.new("file1.txt", "hello world10\n"))
cmds.add_command(CopyFile.new("file1.txt", "file2.txt"))
cmds.add_command(DeleteFile.new("file1.txt"))

cmds.execute
cmds.unexecute

最後にcmds.unexecuteメソッドを呼び出すことで、
コピー処理の一連の流れを取り消すことができます。

東京観光

週末は東京をぶらぶらしてました。


が、今日はあいにくの大雨でした。
なので、雨にぬれないようにと、とりあえず恵比寿ガーデンプレイスに行ってみました。
IMG_0194.JPG


写真が暗いのはiPhoneトイカメラのエフェクトをかけたから。
ここまで暗くはなかったです。


恵比寿に行ったものの、、
意外と何もすることがないので、、iPhoneで写真を撮ってごまかす。
IMG_0196.JPG



iPhoneで写真を撮ってごまかす2。
IMG_0198.JPG



東京駅に移動して新丸ビルでうろうろ。7階のカフェでお茶。
IMG_0199.JPG


新丸ビルの7階んとこは、外に出れるので天気が良かったら楽しそう。
お酒も外で飲めるっぽいし。


東京生活ももうすぐ終わりです。
せっかくなので来週もiPhone片手にどっか行ってみようと思います。

Iterator

Rubyによるデザインパターン生活、何日目とかもういいや。
本日はIteratorパターンを取り上げます。


Rubyによるデザインパターン

Rubyによるデザインパターン


Iteratorパターンは、
「集約オブジェクトがもとにある内部表現を公開せずに、その要素に順にアクセスする方法を提供する」とGoFは定義しています。
Javaでよく出てくるIteratorインタフェースがまさにそれです。
Iteratorインタフェースを利用することでListオブジェクトなど内部を意識せず、順番に要素へアクセスすることができます。


RubyにおけるIteratorパターンでは、外部イテレータと内部イテレータが存在します。
外部イテレータってのは、JavaIteratorインタフェースのようなクラスを自分で作ってしまう方法で、
内部イテレータってのは、Rubyでよく見るeach{|element| #do something}のようなメソッドを意味します。
*1


で、まずは外部イテレータを試してみます。
配列オブジェクト用のイテレータ(ArrayIterator)クラス。

class ArrayIterator
  def initialize(array)
    @array = array
    @index = 0
  end
  
  def has_next?
    @index < @array.length
  end
  
  def item
    @array[@index]
  end
  
  def next_item
    value = @array[@index]
    @index += 1
    value
  end
end


使い方はこんなかんじ。

array = ["red", "green", "blue"]
it = ArrayIterator.new(array)
while it.has_next? 
  puts "item: #{it.next_item}"
end


そんで実行結果。

item: red
item: green
item: blue


ちなみに名前はArrayIteratorだけど、配列オブジェクトにしか使えないわけではなくて、文字列オブジェクトの操作にも使えます。

it = ArrayIterator.new("test")
while it.has_next?
  puts "item:#{it.next_item.chr}"
end


文字列操作の場合は文字コードが返されるので、chrメソッドで文字データに変換しています。
実行結果は以下のとおり。

item:t
item:e
item:s
item:t


続いて、内部イテレータの話です。
Arrayオブジェクトに定義されているeachメソッドのようなものを独自で実装すると以下のようになります。

def for_each_element(array)
  i = 0
  while i < array.length
    yield(array[i])
    i += 1
  end
end

引数で受け取ったArrayオブジェクトに対して、yieldメソッドでコードブロックを順に実行するかんじです。


呼び出し側のプログラムは以下のようになります。

a = [10, 20, 30]
for_each_element(a){ |element| puts element }


実行結果です。

10
20
30


外部イテレータより、内部イテレータの方がRubyっぽいというか便利なように思います。
ただし、2つのArrayオブジェクトをマージするような場合は、
外部イテレータを使う方が楽なようです。

def merge(array1, array2)
  merged = []
  
  it1 = ArrayIterator.new(array1)
  it2 = ArrayIterator.new(array2)
  
  while it1.has_next? && it2.has_next?
    if it1.item < it2.item
      merged << it1.next_item
    else
      merged << it2.next_item
    end
  end
  
  while it1.has_next?
      merged << it1.next_item
  end

  while it2.has_next?
      merged << it2.next_item
  end
  merged
end

a = [10, 20, 30]
b = [11, 12, 21, 22]
merge(a, b).each { |e|  
  puts e
}


マージの実行結果です。

10
11
12
20
21
22
30


Iteratorパターンのお話はこんなところなんだけど、
この章では、Enumerableモジュールの使い方を紹介しています。


Enumerableモジュールをインクルードすると、集約オブジェクトに対して便利なメソッドを取り込むことができます。
たとえば、all?とかany?とかinclude?とか。

Enumerableモジュールをインクルードするには、以下の点を考慮します。

  • 内部イテレータメソッドにeachという名前をつけること。
  • <=>比較演算子の適切な実装をもつこと。
class Account
  attr_accessor :name, :balance
  
  def initialize(name, balance)
    @name = name
    @balance = balance
  end
  
  def <=>(other)
    @balance <=> other.balance
  end
end


class Portfolio
  include Enumerable
  
  def initialize
    @accounts = []
  end
  
  def each(&block)
    @accounts.each(&block)
  end
  
  def add_account(account)
    @accounts << account
  end
end

account1 = Account.new("account1", 1000)
account2 = Account.new("account2", 5000)
account3 = Account.new("account3", 3000)
account4 = Account.new("account4", 4000)
portfolio = Portfolio.new
portfolio.add_account(account1)
portfolio.add_account(account2)
portfolio.add_account(account3)
portfolio.add_account(account4)

puts portfolio.any? { |account| account.balance > 2000 }
puts portfolio.all? { |account| account.balance > 2000 }


実行すると、trueが表示されます。

true
false


あと、この章の最後のところで、ObjectSpaceモジュールも紹介されています。

ObjectSpaceは、Rubyインタープリタの中に存在する完全なオブジェクト空間へのアクセスを提供します。

初めて知ったけど、これけっこう面白い。どこで使えばよいのかはわからないけど。
さっきのプログラムのつづきに以下のコードを追加すると、

ObjectSpace.each_object(Account){ |object| puts object.name }


こんなふうにメモリ上に存在するインスタンスを取得することができます。

account4
account3
account2
account1

以上。

*1:こういう話は言われてみたら、そうだなーって思うけど、言われなかったら意識しないままだと思う。

Composite

Rubyによるデザインパターン生活、、4日目。
#しばらく飲み会続きでお休みしていました。



、、継続します。



Rubyによるデザインパターン

Rubyによるデザインパターン


本日はCompositeパターンを取り上げます。
僕の中では、Compositeというと「集約」というイメージがあります。オブジェクト思考とかで。
Rubyによるデザインパターン本では「部分から全体を組み立てる」と紹介しています。



そんじゃ見ていきます。
本の中では、ケーキを作るというプロセスをツリー構造に分解しています。
詳細化したタスクをCompositeパターンで処理しよう!ってかんじ。

  • MakeCake(ケーキを作る)
    • Make Batter
      • AddDryIngredients
      • AddLiquids
      • Mix
    • Package Cake(ケーキを箱詰めにする)
      • 省略

みたいなかんじで、ケーキを作るための作業を詳細化しています。


Compositeパターンでは3つの部品を使います。

  • すべてのオブジェクトの基底となるクラス(Component)
  • 葉(Leaf)となるクラス
  • 小要素をもつ複合的なクラス(Composite)

今回登場するクラス。

  • Task
    • 基底となるクラス(Component)
  • AddDryIngredientsTask
    • 単体の作業を表すクラス(Leaf
  • AddLiquidsTask
    • 単体の作業を表すクラス(Leaf
  • MixTask
    • 単体の作業を表すクラス(Leaf
  • MakeBatterTask
    • 複数の作業によって構成されるクラス(Composite)


それでは、コードを見てみます。
まずは、Taskクラスです。

class Task
  attr_accessor :name
  def initialize(name)
    @name = name;
  end
  
  def get_time_required
    0.0
  end
end

属性nameとget_time_requiredメソッドを定義しています。
get_time_requiredメソッドは、作業に必要な時間を返します。
Taskクラスは抽象的なクラスのため、作業時間は0を返しています。
#結果としてサブクラスでオーバーライドすることになります。


つづいて、AddDryIngredientsTaskです。

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients')
  end
  
  def get_time_required
    1.0
  end
end

Taskクラスを継承しています。get_time_requiredメソッドをオーバーライドしています。
AddDryIngredientsTaskクラスはCompositeパターンにおけるLeafにあたるクラスです。
そのため小要素は持たず、自身の作業だけを遂行します。

次に紹介するMixTask、AddLiquidsTaskクラスも同様のLeafにあたるクラスです。

class MixTask < Task
  def initialize
    super('Mix that batter up!')
  end

  def get_time_required
    3.0
  end
end
class AddLiquidsTask < Task
  def initialize
    super('Add Liquids task')
  end
  
  def get_time_required
    5.0
  end
end


つづいて、AddDryIngredientsTask、MixTask、AddLiquidsTaskによって構成されるMakeBatterTaskクラスです。

class MakeBatterTask < Task
  def initialize
    super('Make batter')
    @sub_tasks = []
    add_sub_task(AddDryIngredientsTask.new)
    add_sub_task(AddLiquidsTask.new)    
    add_sub_task(MixTask.new)
  end
  
  def add_sub_task(task)
    @sub_tasks << task
  end
  
  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end
  
  def get_time_required
    time = 0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end

MakeBatterTaskクラスはCompositeパターンにおけるCompositeにあたるクラスです。
自身は小要素となるAddDryIngredientsTask、MixTask、AddLiquidsTaskオブジェクトを保持します。
作業時間を求めるget_time_requiredメソッドでは、各小要素に対して作業時間を問い合わせ、合計した結果を返します。


最後に呼び出しもととなるプログラムです。

task = MakeBatterTask.new

puts task.name
puts task.get_time_required


実行結果です。

Make batter
9.0


と、こんなかんじです。


あとは、
MakeBatterTaskクラスからCompositeにあたる部分をCompositeTaskクラスとして切り出すこともできます。

class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end

  def add_sub_task(task)
    @sub_tasks << task
    task.parent_task = self
  end
  
  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end
  
  def get_time_required
    time = 0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end


するとMakeBatterTaskはシンプルになります。

class MakeBatterTask < CompositeTask
  def initialize
    super('Make batter')
    add_sub_task(AddDryIngredientsTask.new)
    add_sub_task(MixTask.new)
    add_sub_task(AddLiquidsTask.new)    
  end
end


また、CompositeTaskには配列を操作するような演算子メソッドを追加すると便利です。
<<メソッドや、[]メソッドなど。

class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end

  def add_sub_task(task)
    @sub_tasks << task
    task.parent_task = self
  end

  def <<(task)
    add_sub_task(task)
  end
  
  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end
  
  def [](index)
    @sub_tasks[index]
  end
  
  def []=(index, new_value)
    @sub_tasks[index] = new_value
  end

  def total_number_basic_tasks
    total = 0
    @sub_tasks.each { |task| total += task.total_number_basic_tasks }
    total
  end

  def get_time_required
    time = 0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end


以上、おしまい。

TextMateの使い方を学ぶ。Hash編、Block編


最近、Rubyで遊んでるんですが、開発環境にTextMateを使っています。
TextMateのBundle(スニペット?)は鬼のように強力なのですが、全然使いこなせていません。。


Googleで調べてもTextMateの日本語での情報はそれほど多くないので、
英語のドキュメント読んで勉強しようかなー、と思ってたら、Youtubeでわかりやすい動画を見つけました。
http://www.youtube.com/user/dneighbo
Derek氏ナイス。Good job!


そんなDerekさんの動画をまとめました。

Hash編

  • Insert Hash Pointer( => の挿入)
    • control + L
  • Insert Hash Pair(Hashペアの挿入)
    • : -> tab*1
  • Create New Hash(Hashの作成)
    • Hash -> tab

Block編

  • Block Insert do(doブロックの挿入)
    • do -> tab
  • Block Insert {({ブロックの挿入)
    • { -> tab
  • Block Toggle { and do(do、{ブロックのトグル)
    • control + {


個人的にはBlock Toggle { and doとかちょっとビビった。
他にも何本か動画がアップされているようなので、またのちほどまとめる。予定。

*1:->は続けて入力の意味です。

Observer

Rubyによるデザインパターン生活3日目。in 市ヶ谷。
今日はObserverパターンを取り上げます。

Rubyによるデザインパターン

Rubyによるデザインパターン


Observerってのは「観測者」っていう意味が一番しっくりくるかな。
あるオブジェクトの状態変化を観測し、
変化が発生したことを知らせるのが目的です。


著者のラス・オルセンさんも言ってるけど、
Observerとなるクラスが観測者にあたるんだけど、
実際に観測者に変化を通知するのは、観測対象となるオブジェクト(Observable)なります。
#その辺でObserverパターンのネーミングは若干微妙な気もします。


そんな話はさておき、今回登場するクラスは以下のとおりです。

  • Employee
    • 従業員クラス。名前、役職、給料といった状態をもつ。状態が変化したら観測者(Observer)へ通知する。
  • Payroll
    • 給与明細クラス。従業員の給料の変化などを観測するObserverクラス。
  • TaxMan
    • 税金クラス。従業員の給料の変化などを観測するObserverクラス。


つづいてソースコードです。
まず、Employeeクラス。

class Employee
  attr_reader :name
  attr_accessor :title, :salary
  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
    @observers = []
  end
  
  def salary=(new_salary)
    @salary = new_salary
    notify_observers
  end
  
  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end
  
  def add_observer(observer)
    @observers << observer
  end
  
  def delete_observer(observer)
    @observers.delete(observer)
  end
end

インスタンス変数@observersがポイント。
ここに観測者(Observer)となるオブジェクトを保持します。
@observersへの追加にはadd_observerメソッドを使います。
状態であるsalaryが変化した場合、notify_observersメソッドが呼び出されるので、
結果として観測者クラス(Observer)のupdateメソッドが呼び出されます。


つづいて、観測者(Observer)となるPayrollクラスです。

class Payroll
  def update(changed_employee)
    puts "#{changed_employee.name}のために小切手を切ります。"
    puts "給料は#{changed_employee.salary}です。"
  end
end

変更通知用のupdateメソッドを持っています。
引数には観測対象であるEmployeeオブジェクトを受け取ります。


つづいてTaxManクラス。こちらもObserverクラスになります。

class TaxMan
  def update(changed_employee)
    puts "#{changed_employee.name}に新しい税金の請求書を送ります。"
  end
end

Payrollクラスと同じくupdateメソッドを実装しています。


実行用のプログラムです。

fred = Employee.new("Fred", "Crane Operator", 30000.0)

payroll = Payroll.new
fred.add_observer(payroll)
taxman = TaxMan.new
fred.add_observer(taxman)

fred.salary = 35000.0


そんで、実行結果です。

Fredのために小切手を切ります。
給料は35000.0です。
Fredに新しい税金の請求書を送ります。


以上が一般的なObserverパターンの流れです。
Payroll、TaxMan以外の観測者(Observer)クラスが追加された場合も、同様に処理できます。


ここまでのコードでまずいところは、
Employeeクラスの中に観測対象者(Observable)として状態や振る舞いが実装されてしまっているところ。
Employeeクラスが読みにくいものになっています。


なんで、コードを分離してあげます。
観測対象者(Observable)に該当するコードを抽出します。
今回はSubject(観測対象)という意味あいのクラスとして抽出します。


が、Subjectクラスをスーパークラスとして抽出してしまうと、
継承関係の制約(単一継承しかできない)を受けるためよろしくありません。


なんで、Rubyに用意されているモジュール機能を使います。
Rubyは複数のモジュールをインクルード可能です。
モジュールはJavaのインタフェースに近いような?気がします。
#実装つきのインタフェースってかんじ?


Subjectモジュール。

module Subject
  def initialize
    @observers = []
  end
  
  def add_observer(observer)
    @observers << observer
  end
  
  def delete_observer(observer)
    @observers.delete(observer)
  end
  
  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end
end


Employeeクラス。Subjectモジュールをインクルードしています。

class Employee
  include Subject
  
  attr_reader :name
  attr_accessor :title, :salary

  def initialize(name, title, salary)
    super()
    @name = name;
    @title = title
    @salary = salary
  end
  
  def salary=(new_saraly)
    @salary = new_saraly
    notify_observers
  end
end


以上がObserverパターンのRubyっぽいモジュールを使った方法になります。
ちなみにRubyのライブラリの中にもObservableモジュールが用意されています。


話が少し変わりますが、
RubyでObserverパターンを実現する場合、
コードブロックをObserverクラスに見立てて処理することもできます。
Subjectクラスをこんなかんじに変更します。

module Subject
  def initialize
    @observers = []
  end
  
  def add_observer(&observer)
    @observers << observer
  end
  
  def delete_observer(observer)
    @observers.delete(observer)
  end
  
  def notify_observers
    @observers.each do |observer|
      observer.call(self)
    end
  end
end

add_observer(&observer)メソッドの引数がコードブロックの参照になっているのと、
notify_observersメソッドの中でObserverとなるコードブロックを実行しています。


呼び出しもとのプログラムはこんなかんじになります。

require "employee"
fred = Employee.new("Fred", "Crane Operator", 30000.0)
fred.add_observer do |changed_employee|
  puts "#{changed_employee.name}のために小切手を切ります。"
  puts "給料は#{changed_employee.salary}です。"
end

fred.salary = 35000.0

fred.add_observerの引数にコードブロックを渡しています。
これがObserverとして機能します。


実行結果はさきほどと同じ。

Fredのために小切手を切ります。
給料は35000.0です。


以上で、Observerパターンおしまい。


ちなみに、コードを読んで、ブログにまとめると大体2時間くらいかかります。
それが勉強になっていると信じてあと10コくらいのパターンの紹介を続けます。


あと、クラス図を簡単に書けるツール(Webで使える)あればいいなー。と。


おしまい。