Proc
本来オブジェクトにできないブロックをオブジェクト化するもの。これにより再利用性を高め、柔軟で動的なコードにすることができる。
ブロックとは?
- ブロックは、メソッドに渡すことができるコードの塊
- {} または do..end で囲まれたコードで表される
- メソッドは yield を使用してブロックを呼び出すことができる
- Rubyは全てのコードがオブジェクトと言われているが、例外的にブロックはオブジェクトではない。従来であれば、ブロックはオブジェクトにできなかったが、Procを用いることでブロックをオブジェクトとして扱うことができる
# do..endの箇所がブロックで、timesの引数
3.times do |i|
puts a
end
#=> 0 1 2
# {}の箇所がブロックで、timesの引数
3.times { |i| puts i }
#=> 0 1 2
何が嬉しいの?ある時代とない時代の処理の違い
可搬性・再利用性・柔軟性
Procがない時代には、ブロックをオブジェクト化できないため、同じような処理を行う場合に同じコードを何度も書かなければならなかったり、特定の構造の中でしかコードを実行できないという制限があった
しかし、Procを使うと、コードの断片を変数に割り当てたり、メソッドの引数として渡したりすることができるため、プログラムの可搬性・再利用性・柔軟性の向上を期待できるようになった。
メソッドを使った場合(その1)
def handle_text(text, mode)
# modeが増えるたびに条件分岐を増やさなければならない
if mode == :uppercase
puts text.upcase
elsif mode == :reverse
puts text.reverse
else
puts text
end
end
handle_text("hello", :uppercase) # "HELLO"
handle_text("hello", :reverse) # "olleh"
handle_text("hello", :normal) # "hello"
# modeが増えるたびにhandle_textに条件分岐を追加しなければならないが、これはメソッドの柔軟性やテスタビリティを損なう。
Procを使った場合(その1)
def handle_text(text, operation)
operation.call(text)
end
uppercase_proc = Proc.new { |t| puts t.upcase }
reverse_proc = Proc.new { |t| puts t.reverse }
handle_text("hello", uppercase_proc) # "HELLO"
handle_text("hello", reverse_proc) # "olleh"
# Procの利用は、プログラムの柔軟性を高め、既存のメソッドを変更せずに新しい機能を追加できるようにするという点で、オープン・クローズドの原則と似た目的を持っている
メソッドを使った場合(その2)
# 通常のメソッド
def multiply(number, factor)
number * factor
end
def divide(number, factor)
number / factor
end
def add(number, factor)
number + factor
end
def perform_calculation(number, factor, operation)
case operation
when :multiply
result = multiply(number, factor)
when :divide
result = divide(number, factor)
when :add
result = add(number, factor)
end
puts "結果: #{result}"
end
# 異なる計算を実行
perform_calculation(10, 3, :multiply) # 乗算: 結果: 30
perform_calculation(10, 3, :divide) # 除算: 結果: 3.333...
perform_calculation(10, 3, :add) # 加算: 結果: 13
Procを使った場合(その2)
# Procの例
multiply = Proc.new { |number, factor| number * factor }
divide = Proc.new { |number, factor| number / factor }
add = Proc.new { |number, factor| number + factor }
def perform_calculation(number, operation)
result = operation.call(number, 3)
puts "結果: #{result}"
end
# 異なる計算を実行
perform_calculation(10, multiply) # 乗算: 結果: 30
perform_calculation(10, divide) # 除算: 結果: 3.333...
perform_calculation(10, add) # 加算: 結果: 13