ruby中的delegate
用途区别:
delegator用于委托几乎所有方法到一个对象上,此种做法无法继承其他类
Delegator相当于一个抽象类(一般不用),它定义了子类在new时需要指定被委托对象,然后通过method_missing来委托到__getobj__上
SimpleDelegator:实现了__setobj__,用以切换被委托的对象,
DelegateClass:实现了__setobj__,用以切换被委托的对象,通过define_method来委托,会快一些
Forwardable用于委托某些方法到不同对象上,可精细地指定哪个方法去哪个对象
Delegator源码:
# 避免Object里的方法造成干扰,所以继承BasicObject
class Delegator < BasicObject
# 但又想获取Kernel的某些方法(不想一一列出,所以用排除法undef_method)
kernel = ::Kernel.dup
kernel.class_eval do
alias __raise__ raise
# 以下方法的表现应由被委托的对象来呈现
# 如果不去掉,那么内省、比较都会使用Delegator示例,而非被委托的对象
[:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m|
undef_method m
end
# Delegator.ancestors[1].private_instance_methods
private_instance_methods.each do |m|
if /\Ablock_given\?\z|iterator\?\z|\A__.*__\z/ =~ m
next
end
undef_method m
end
end
include kernel
def self.const_missing(n)
::Object.const_get(n)
end
def initialize(obj)
__setobj__(obj)
end
# 委托实现于此
def method_missing(m, *args, &block)
r = true
target = self.__getobj__ {r = false}
begin
if r && target.respond_to?(m)
target.__send__(m, *args, &block)
elsif ::Kernel.respond_to?(m, true)
::Kernel.instance_method(m).bind(self).(*args, &block)
else
super(m, *args, &block)
end
ensure
$@.delete_if {|t| %r"\A#{Regexp.quote(__FILE__)}:(?:#{[__LINE__-7, __LINE__-5, __LINE__-3].join('|')}):"o =~ t} if $@
end
end
# 在Delegator上调用respond_to?时,基本上都会false,因为继承自BasicObject
# 所以会走respond_to_missing?
def respond_to_missing?(m, include_private)
r = true
target = self.__getobj__ {r = false}
r &&= target.respond_to?(m, include_private)
if r && include_private && !target.respond_to?(m, false)
warn "#{caller(3)[0]}: delegator does not forward private method \##{m}"
return false
end
r
end
# 只有methods、public_methods和protected_methods需要转发
def methods(all=true)
__getobj__.methods(all) | super
end
def public_methods(all=true)
__getobj__.public_methods(all) | super
end
def protected_methods(all=true)
__getobj__.protected_methods(all) | super
end
def ==(obj)
return true if obj.equal?(self)
self.__getobj__ == obj
end
def !=(obj)
return false if obj.equal?(self)
__getobj__ != obj
end
def !
!__getobj__
end
# 抽象接口,需要继承重写
def __getobj__
__raise__ ::NotImplementedError, "need to define `__getobj__'"
end
# 抽象接口,需要继承重写
def __setobj__(obj)
__raise__ ::NotImplementedError, "need to define `__setobj__'"
end
#
# Serialization support for the object returned by \_\_getobj\_\_.
#
def marshal_dump
ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
[
:__v2__,
ivars, ivars.map{|var| instance_variable_get(var)},
__getobj__
]
end
#
# Reinitializes delegation from a serialized object.
#
def marshal_load(data)
version, vars, values, obj = data
if version == :__v2__
vars.each_with_index{|var, i| instance_variable_set(var, values[i])}
__setobj__(obj)
else
__setobj__(data)
end
end
def initialize_clone(obj) # :nodoc:
self.__setobj__(obj.__getobj__.clone)
end
def initialize_dup(obj) # :nodoc:
self.__setobj__(obj.__getobj__.dup)
end
private :initialize_clone, :initialize_dup
# 此4个方法是Object才有
# 当然,即使BasicObject有,也应pass给被委托对象
[:trust, :untrust, :taint, :untaint, :freeze].each do |method|
define_method method do
__getobj__.send(method)
super()
end
end
# 只是alias
@delegator_api = self.public_instance_methods
def self.public_api
@delegator_api
end
end
# SimpleDelegator只是实现了Delegator的get/set
#
# 用法举例:
#
# 1. 桥接/适配,满足调用者的接口
#
# class User
# def born_on
# Date.new(1989, 9, 10)
# end
# end
#
# class UserDecorator < SimpleDelegator
# def birth_year
# born_on.year
# end
# end
#
# decorated_user = UserDecorator.new(User.new)
# decorated_user.birth_year #=> 1989
# decorated_user.__getobj__ #=> #<user: ...="">
#
# 2. 拦截/装饰。重写接口,
# 当调用super时,因SimpleDelegator无该方法
# 会进入method_missing,即交由被委托对象来处理
#
# class SuperArray < SimpleDelegator
# def [](*args)
# super + 1
# end
# end
#
# SuperArray.new([1])[0] #=> 2
#
class SimpleDelegator<delegator ="" #="" block可以是某些容错或其它回调触发机制="" ="" #="" 如delegator#method_missing的{r="false}" ="" def="" __getobj__="" ="" ="" unless="" defined?(@delegate_sd_obj)="" ="" ="" ="" return="" yield="" if="" block_given?="" ="" ="" ="" __raise__="" ::argumenterror,="" "not="" delegated"="" ="" ="" end="" ="" ="" @delegate_sd_obj="" ="" end="" ="" #="" 切换被委托的对象,新对象与旧对象应有相同接口="" ="" def="" __setobj__(obj)="" ="" ="" __raise__="" ::argumenterror,="" "cannot="" delegate="" to="" self"="" if="" self.equal?(obj)="" ="" ="" @delegate_sd_obj="obj" ="" end="" end="" def="" delegator.delegating_block(mid)="" #="" :nodoc:="" ="" lambda="" do="" |*args,="" &block|="" ="" ="" target="self.__getobj__" ="" ="" begin="" ="" ="" ="" target.__send__(mid,="" *args,="" &block)="" ="" ="" ensure="" ="" ="" ="" $@.delete_if="" {|t|="" \a#{regexp.quote(__file__)}:#{__line__-2}:="" o="~" t}="" if="" $@="" ="" ="" end="" ="" end="" end="" #="" 返回一个继承delegator的class(这样initialize时,会__setobj__,保存被委托的对象)="" #="" 并且该class拥有superclass的所有“领域接口”(使得定义该class子类时,可定义一些adapter接口)="" #="" 例如:="" #="" # ="" class="" q="" <="" delegateclass(array)="" #="" step="" 1="" # ="" ="" def="" enq="" obj="" #="" ="" ="" __getobj__="" <<="" obj="" # ="" ="" end="" #="" ="" ="" def="" deq="" #="" ="" ="" ="" _getobj__.shift="" #="" ="" ="" end="" # ="" end="" #="" #="" ="" q="Q.new" [1,2,3]="" #="" ="" q.enq="" 4="" #="" 1,2,3,4="" #="" ="" q.deq="" #="" 2,3,4="" #="" def="" delegateclass(superclass)="" ="" klass="Class.new(Delegator)" ="" methods="superclass.instance_methods" ="" methods="" -="::Delegator.public_api" ="" methods="" -="[:to_s,:inspect,:=~,:!~,:===]" ="" klass.module_eval="" do="" ="" ="" def="" __getobj__ ="" #="" :nodoc:="" ="" ="" ="" unless="" defined?(@delegate_dc_obj)="" ="" ="" ="" ="" return="" yield="" if="" block_given?="" ="" ="" ="" ="" __raise__="" ::argumenterror,="" "not="" delegated"="" ="" ="" ="" end="" ="" ="" ="" @delegate_dc_obj="" ="" ="" end="" ="" ="" def="" __setobj__(obj) ="" #="" :nodoc:="" ="" ="" ="" __raise__="" ::argumenterror,="" "cannot="" delegate="" to="" self"="" if="" self.equal?(obj)="" ="" ="" ="" @delegate_dc_obj="obj" ="" ="" end="" ="" ="" methods.each="" do="" |method|="" ="" ="" ="" define_method(method,="" delegator.delegating_block(method))="" ="" ="" end="" ="" end="" ="" klass.define_singleton_method="" :public_instance_methods="" do="" |all="true|" ="" ="" super(all)="" -="" superclass.protected_instance_methods="" ="" end="" ="" klass.define_singleton_method="" :protected_instance_methods="" do="" |all="true|" ="" ="" super(all)="" |="" superclass.protected_instance_methods="" ="" end="" ="" return="" klass="" end<="" code=""></delegator></user:>
Forwardable源码:
# 假设类RecordCollection的对象含有数组@records
# 那么你可以用def_delegator
# 将record_number()指向@records的的[],如下:
#
# require 'forwardable'
#
# class RecordCollection
# attr_accessor :records
# extend Forwardable
# def_delegator :@records, :[], :record_number
# end
#
# 使用时:
#
# r = RecordCollection.new
# r.records = [4,5,6]
# r.record_number(0) # => 4
#
# 如果不是方法改名,而只是简单委派,用def_delegators :
#
# class RecordCollection # re-open RecordCollection class
# def_delegators :@records, :size, :<<, :map
# end
#
# r = RecordCollection.new
# r.records = [1,2,3]
# r.record_number(0) # => 1
# r.size # => 3
# r << 4 # => [1, 2, 3, 4]
# r.map { |x| x * 2 } # => [2, 4, 6, 8]
#
# 甚至可以针对单个对象来定义委派:
# (使用def_delegator但不需改方法名时,可不指定)
#
# my_hash = Hash.new
# my_hash.extend Forwardable # prepare object for delegation
# my_hash.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
# my_hash.puts "Howdy!"
#
# 相比继承,使用组合与委派能更精细的控制对象能响应哪些方法:
#
# class Queue
# extend Forwardable
#
# def initialize
# @q = [ ] # prepare delegate object
# end
#
# # setup preferred interface, enq() and deq()...
# def_delegator :@q, :push, :enq
# def_delegator :@q, :shift, :deq
#
# # support some general Array methods that fit Queues well
# def_delegators :@q, :clear, :first, :push, :shift, :size
# end
#
# q = Queue.new
# q.enq 1, 2, 3, 4, 5
# q.push 6
#
# q.shift # => 1
# while q.size > 0
# puts q.deq
# end
#
# q.enq "Ruby", "Perl", "Python"
# puts q.first
# q.clear
# puts q.first
#
# This should output:
#
# 2
# 3
# 4
# 5
# 6
# Ruby
# nil
#
# == Notes
#
# Be advised, RDoc will not detect delegated methods.
#
# +forwardable.rb+ provides single-method delegation via the def_delegator and
# def_delegators methods. For full-class delegation via DelegateClass, see
# +delegate.rb+.
#
module Forwardable
# Version of +forwardable.rb+
FORWARDABLE_VERSION = "1.1.0"
FILE_REGEXP = %r"#{Regexp.quote(__FILE__)}"
# 设了debug的话,才保留报错栈中的本程序信息
@debug = nil
class << self
# If true, __FILE__ will remain in the backtrace in the event an
# Exception is raised.
attr_accessor :debug
end
# delegate method => accessor
# delegate [method, method, ...] => accessor
# 参数为一hash,key是(一组)方法名的symbol,value是被委托对象
def instance_delegate(hash)
hash.each{ |methods, accessor|
methods = [methods] unless methods.respond_to?(:each)
methods.each{ |method|
def_instance_delegator(accessor, method)
}
}
end
# 一次过定义多个转发方法,但不能指定新方法名
# 排除重定义__send__和__id__
# 但其实还是可以通过def_instance_delegator和instance_delegate
# 不过eval的时候会警告
def def_instance_delegators(accessor, *methods)
methods.delete("__send__")
methods.delete("__id__")
for method in methods
def_instance_delegator(accessor, method)
end
end
# 直接掉调此方法来定义的话,可指定新接口
#
# class MyQueue
# extend Forwardable
# attr_reader :queue
# def initialize
# @queue = []
# end
#
# def_delegator :@queue, :push, :mypush
# end
#
# q = MyQueue.new
# q.mypush 42
# q.queue #=> [42]
# q.push 23 #=> NoMethodError
#
def def_instance_delegator(accessor, method, ali = method)
line_no = __LINE__; str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue Exception
# 将报错栈中有关本文件的行去掉,使报错更清晰
$@.delete_if{|s| Forwardable::FILE_REGEXP =~ s} unless Forwardable::debug
::Kernel::raise
end
end
}
# 如果是class或module,则直接定义实例方法,否则,定义一般对象的单例方法
# 这意味着,并不一定要让实例去extend SingleForwardable来获得单例特有的转发机制
begin
module_eval(str, __FILE__, line_no)
rescue
instance_eval(str, __FILE__, line_no)
end
end
alias delegate instance_delegate
alias def_delegators def_instance_delegators
alias def_delegator def_instance_delegator
end
# 通过extend SingleForwardable,使对象(非class)获得其单例特有的转发机制
#
# 一般用法就是把方法转发给实例变量,但是转给全局对象也可以
# 如下,使printer的puts方法引用STDOUT的puts:
#
# printer = String.new
# printer.extend SingleForwardable # prepare object for delegation
# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
# printer.puts "Howdy!"
#
# 同理,以下Implementation和Facade位于同一层次
# 可用SingleForwardable使Facade的service方法引用Implementation的service
# 这用Forwardable是做不到的(因为Forwardable优先module_eval,定义实例方法)
#
# class Implementation
# def self.service
# puts "serviced!"
# end
# end
#
# module Facade
# extend SingleForwardable
# def_delegator :Implementation, :service
# end
#
# Facade.service #=> serviced!
#
# If you want to use both Forwardable and SingleForwardable, you can
# use methods def_instance_delegator and def_single_delegator, etc.
module SingleForwardable
def single_delegate(hash)
hash.each{ |methods, accessor|
methods = [methods] unless methods.respond_to?(:each)
methods.each{ |method|
def_single_delegator(accessor, method)
}
}
end
def def_single_delegators(accessor, *methods)
methods.delete("__send__")
methods.delete("__id__")
for method in methods
def_single_delegator(accessor, method)
end
end
def def_single_delegator(accessor, method, ali = method)
str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue Exception
$@.delete_if{|s| Forwardable::FILE_REGEXP =~ s} unless Forwardable::debug
::Kernel::raise
end
end
}
# 直接instance_eval定义单例方法
instance_eval(str, __FILE__, __LINE__)
end
alias delegate single_delegate
alias def_delegators def_single_delegators
alias def_delegator def_single_delegator
end