
Ruby 责任链模式讲解和代码示例
责任链是一种行为设计模式, 允许你将请求沿着处理者链进行发送, 直至其中一个处理者对其进行处理。
该模式允许多个对象来对请求进行处理, 而无需让发送者类与具体接收者类相耦合。 链可在运行时由遵循标准处理者接口的任意处理者动态生成。
复杂度:
流行度:
使用示例: 责任链模式在 Ruby 程序中并不常见, 因为它仅在代码与对象链打交道时才能发挥作用。
识别方法: 该模式可通过一组对象的行为方法间接调用其他对象的相同方法来识别, 而且所有对象都会遵循相同的接口。
概念示例
本例说明了责任链设计模式的结构并重点回答了下面的问题:
- 它由哪些类组成?
- 这些类扮演了哪些角色?
- 模式中的各个元素会以何种方式相互关联?
main.rb: 概念示例
# The Handler interface declares a method for building the chain of handlers. It
# also declares a method for executing a request.
class Handler
# @abstract
#
# @param [Handler] handler
def next_handler=(handler)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# @abstract
#
# @param [String] request
#
# @return [String, nil]
def handle(request)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# The default chaining behavior can be implemented inside a base handler class.
class AbstractHandler < Handler
# @return [Handler]
attr_writer :next_handler
# @param [Handler] handler
#
# @return [Handler]
def next_handler(handler)
@next_handler = handler
# Returning a handler from here will let us link handlers in a convenient
# way like this:
# monkey.next_handler(squirrel).next_handler(dog)
handler
end
# @abstract
#
# @param [String] request
#
# @return [String, nil]
def handle(request)
return @next_handler.handle(request) if @next_handler
nil
end
end
# All Concrete Handlers either handle a request or pass it to the next handler
# in the chain.
class MonkeyHandler < AbstractHandler
# @param [String] request
#
# @return [String, nil]
def handle(request)
if request == 'Banana'
"Monkey: I'll eat the #{request}"
else
super(request)
end
end
end
class SquirrelHandler < AbstractHandler
# @param [String] request
#
# @return [String, nil]
def handle(request)
if request == 'Nut'
"Squirrel: I'll eat the #{request}"
else
super(request)
end
end
end
class DogHandler < AbstractHandler
# @param [String] request
#
# @return [String, nil]
def handle(request)
if request == 'MeatBall'
"Dog: I'll eat the #{request}"
else
super(request)
end
end
end
# The client code is usually suited to work with a single handler. In most
# cases, it is not even aware that the handler is part of a chain.
def client_code(handler)
['Nut', 'Banana', 'Cup of coffee'].each do |food|
puts "\nClient: Who wants a #{food}?"
result = handler.handle(food)
if result
print " #{result}"
else
print " #{food} was left untouched."
end
end
end
monkey = MonkeyHandler.new
squirrel = SquirrelHandler.new
dog = DogHandler.new
monkey.next_handler(squirrel).next_handler(dog)
# The client should be able to send a request to any handler, not just the first
# one in the chain.
puts 'Chain: Monkey > Squirrel > Dog'
client_code(monkey)
puts "\n\n"
puts 'Subchain: Squirrel > Dog'
client_code(squirrel)
output.txt: 执行结果
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut
Client: Who wants a Banana?
Monkey: I'll eat the Banana
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.