Ruby 中 block 是非常重要的,也是非常特别的一部分内容。 很多代码的重构都可以通过 code block 得以一种优雅的方式实现。 根据《Ruby 最佳实践》中的内容,我简单做一些小结。
首先,最常见的就是用于遍历。
(1..5).to_a.map { |i| i * 2 } class SortedList def initialize @data = [] end def <<(element) (@data << element).sort! end def each @data.each { |e| yield(e) } end end
其次,用于在方法的预处理和后处理操作中插入代码
File.open("some.txt") { |f| f << "some words" }
有时也用于临时提升变量的可见范围(scope)(在元编程中比较常见)
"This is a string".instance_eval do "O hai, can has reverse? #{ revers }. kthxbye" end #=> "O hai, can has reverse? gnirts a si sihT. kthsbye"
最后,就是作为模板
>> foo = Hash.new { |h, k| h[k] = [] } => {} >> foo[:bar] => [] >> foo[:bar] << 1 << 2 << 3 => [1, 2, 3]
用 Block 重构代码的几个范例
用 block 来重构 Pre 和 Post 处理过程
require "socket" class Client def initialize(ip = "127.0.0.1", port = 3333) @ip, @port = ip, port end def send_message(msg) socket = TCPSocket.new(@ip, @port) socket.puts(msg) response = socket.gets ensure socket.close if socket end def receive_message socket = TCPSocket.new(@ip, @port) response = socket.gets ensure socket.close if socket end end
这个代码明显不符合 DRY 的风格,所以利用 block 来重构一下:
require "socket" class Client def initialize(ip = "127.0.0.1", port = 3333) @ip, @port = ip, port end def send_message(msg) connection do |socket| socket.puts(msg) socket.gets end end def receive_message connection { |socket| socket.gets } end private def connetcion socket = TCPSocket.new(@ip, @port) yield(socket) ensure socket.close if socket end end if __FILE__ == $0 client = Client.new ["Hello", "My name is Greg", "Goodbye"].each do |msg| response = client.send_message(msg) puts response end end #输出以下内容 #Hello from server at Wed Jul 23 16:15:37 ... #Nice to meet you Greg! #Goodbye from server at Wed Jul 32 ...
以上代码明显简洁了很多
用于动态回调
require 'socket' class Server def initialize(port = 3333) @server = TCPServer.new("127.0.0.1", port) @handlers = {} end def handle(pattern, &block) @handlers[pattern] = block end def run while session = @server.accept msg = session.gets match = nil @handlers.each do |pattern, block| if match = msg.match(pattern) break session.puts(block.call(match)) end unless match session.puts "Server received unknown message: #{msg}" end end end end end if __FILE__ == $0 server = Server.new server.handle(/hello/i){ "Helllo from server at #{Time.now}" } server.handle(/goodbye/i){ "Goodbye from server at #{Time.now}" } server.handle(/name is (\w+)/){ |m| "Nice to meet you #{m[1]}!" } server.run end
用于简化接口
server = Server.new server.handle(/hello/i){ "Helllo from server at #{Time.now}" } server.handle(/goodbye/i){ "Goodbye from server at #{Time.now}" } server.handle(/name is (\w+)/){ |m| "Nice to meet you #{m[1]}!" } server.run #这段代码可以简化为以下版本 Server.run do handle(/hello/i){ "Helllo from server at #{Time.now}" } handle(/goodbye/i){ "Goodbye from server at #{Time.now}" } handle(/name is (\w+)/){ |m| "Nice to meet you #{m[1]}!" } end #class Server修改如下 class Server #other methods same as before #声明为类方法 def self.run(port=3333, &block) server = Server.new(port) #使用instance_eval和&block来重构代码 server.instance_eval(&block) server.run end end