Delegator中文名可以叫托管,委托.
在JAVA中是一种比较高深的设计模式.跟继承的思想有一点点像,但远比继承来的灵活.
简单来理解,可以与现实世界来类比,你交给另一个类帮你打点点事件,有点像助理.
这个助理可以帮几个人同时打点事件.也可以自己额外做些事件.
今天在项目中,遇到了一个类似的问题:
以前的代码:
o = M1::M2::Klass.new
o.method( :a=> 1 )
o.method( :a=> 2 )
要求在尽可能不改动原有代码的基础上,增加功能:
1. 检查每次调用的方法的参数是否符合它的原有代码的注释
注释类似于(非常规范严格)
#id=>a,name=>参数,type=>s,must=>true,default=>"",value=>"{text}",descrip=>"ok"
这个注释是说方法必须具有 hash[:a]这个参数,如果没有就应当报错.
乍一看,不改变表现代码就增加功能? 怎么可能嘛....
No,由于开发使用的ruby,并且采用的惯例配置,使这个需求成为可能.
我们的项目惯例如下:
所有的类名与自己所在的文件名一致,目录与所在的模块一致.
即:
M1:M2:Klass 所在文件为 /m1/m2/klass.rb
以下看如何实现:
首先,由于M1::M2::Klass本身设置为按需加载(如何实现?)
代码如下:
class Module
def const_missing(name)
name = self.name.to_s + "::" + name.to_s
exception = NameError.new("uninitialized constant #{name}")
begin
#require File.join(ATT::ConfigureManager.root,'keywords','keyword',name.underscore)
load_module(name.to_s)
rescue ATT::Exceptions::LoadError
raise $!
rescue ATT::Exceptions::NotFoundError,Exception
raise exception
end
end
end
class Object
def self.const_missing(name)
exception = NameError.new("uninitialized constant #{name}")
begin
load_module(name.to_s)
rescue ATT::Exceptions::LoadError
raise $!
rescue ATT::Exceptions::NotFoundError,Exception
raise exception
end
end
end
简单解决下,利用Module#const_missing与Object.const_missing, 当ruby解释器在找不到定义的类或模块时,会调用两个方法,你可以重新定义使得自动加载成为现实.当然有人会说用AutoLoad啊,那我问你,用autoload不是要自己写额外代码吗? 况且这里的load_module实现了更多功能,如相关依赖的加载,模块的自动关联等.由于这里讨论与此无关,不说load_module的细节了.
好了,第一步已经完成.
那么,如何才能进行委托呢?
第二步,偷天换日,load_module并不实际返回要加载的对象,而是返回它的委托对象.即
M1::M2::Klass.new
返回的不是Klass对象,而是一个实例化的KlassProxy, 见核心实现:
def load_module( klass_str )
#前面忽略大量关联加载的代码
#生成Klass
instance = eval(klass_str)
#转给委托类
keyword_proxy_for_class_name( instance )
end
def keyword_proxy_for_class_name( klass )
KeywordProxy.new( klass ) do |m,hash|
#处理委托回调代码,这里省略
end
end
第二步后,换日已经成功,一切只差委托的实现了.
第三步,委托类,请提供帮忙额外检查方法参数
由于ruby内置了Delegator,那么我们自定义实现太简单了,看:
class KeywordProxy
def self.proxy( klass,&block ) # :yields: hash
ret_obj = KeywordProxyBase.new( klass.new )
ret_obj.before_call(&block)
ret_obj
end
class << self
alias __proxy__ proxy
end
#对外的委托接口,设置要委托的对象,以及要委托的方法执行前置代码.
def initialize( klass, &block )
@klass = klass
@block = block
class <<self
#这里采用匿名方法扩展,使实例化的keyword的类依然支持new,对外代码极其自然
def new
KeywordProxy.proxy(@klass, &@block)
end
end
end
end
class KeywordProxyBase < Delegator
def initialize( obj )
@__obj__ = obj
#这里不要复制委托的办法,只需回传即可,所以设置为nil
super(nil)
@before_call_blocks = []
@after_call_blocks = []
end
def __getobj__
@__obj__
end
def __setobj__(obj)
@__obj__ = obj
end
def before_call(&block)
if block
@before_call_blocks << block
end
end
def after_call(&block)
if block
@after_call_blocks << block
end
end
def method_missing(m, *args)
# 这里才是真正的回调,某一个方法要执行时,要帮助它执行回调.然后才给它.
@before_call_blocks.each do |block|
block.call m,*args
end
ret = super(m,*args)
@after_call_blocks.each do |block|
block.call ret
end
ret
end
end
好了,4个步骤一完,至此整个需求完成了.
我们实现了 "不改变任何表现代码的情况下,偷天换日的实现了往实际调用方法中添加自己的回调." 这种实现不亚于rails任何的官方代码,虽然有些过度利用ruby的动态表达能力,但极少的影响用户接口,简洁至极.
我想,如果你对这部分代码毫无疑问的理解的话,内置的Delegator类就非常easy看懂了.所以这里就不要复述了,接下来再找点时间简单看一下Delegator本身的实现吧.
分享到:
相关推荐
python库。 资源全名:delegator.py-0.0.1.tar.gz
资源分类:Python库 所属语言:Python 资源全名:delegator.py-0.0.13.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
dom-delegator, 使用委托事件装饰元素 dom代理使用委托事件装饰元素dom-delegator 允许你将 EventHandler 附加到dom元素。当发生正确类型的事件时,dom-delegator 将调用你的EventHandler这允许你从事件编写器分离...
资源来自pypi官网。 资源全名:delegator.py-0.0.6.tar.gz
它与Ruby中的委托语法非常相似。 delegate类上的委托属性可以是序列或单个字符串,其参数由空格分隔(类似于namedtuple )。 Delegatee属性必须是第一个参数。 如果您不喜欢这种样式-不用担心-有三种可互换的表示...
请注意: Angular Delegator 是一个非常年轻的项目。 API 可能会在不久的将来发生变化。 例子 如果您有一个大型验证服务来检查帐户的名称、日期、地址和付款信息,您可以将其分解如下: angular . module ( '...
使用Delegator或SimpleDelegator时,不会转发这些方法。 require "delegate"class MyAwesomeClass # ...endo = MyAwesomeClass . newd = SimpleDelegator . new ( o )d . class #=> SimpleDelegatord . is_a? ...
"require-dev" : { "by-cedric/delegator" : "dev-master"} 委托人扩展了Laravel的基本Response外观。 因此,您需要使用Response外观的Delegator版本。 当然,您仍然可以使用Response的标准功能。 在您的config / ...
dom-委托人绑定 是一个用于事件处理委托的库。 对虚拟 dom 有用。
#redux-delegator 以结构化方式组成减速器 const companiesReducer = handleActions({ ADD_COMPANY: (state, {payload:{doc, ASIN}}) => state.set(ASIN, doc), REMOVE_COMPANY: (state, {payload:{ASIN}}) => ...
重新主题 使用Ruby的SimpleDelegator的Uber简单演示者。... SimpleDelegator是Delegator类的具体实现。 基本上,它将任何方法调用委托给传递给构造函数的对象: require 'delegate' array = SimpleDelega
三、利用OFBiz实现SingleSignOn单点登录 60 1、SSO简介 60 2、利用OFBiz建立简单有效的SSO机制 60 4、备注 62 第六部分、安全控制 62 一、认证 62 1、实体 63 2、应用 63 二、权限 63 1、权限的定义方法 63 2、权限...
读取代码始终占据大部分时间,并增强了读取和理解大型代码,读取逻辑的能力。 我认为没有人愿意成为终身用户,即使它是顶级用户也是如此。 当然,无论一个人有多厉害,还是一件罚款。 这是收集source explanation...
a)把<delegator name="default" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main" distributed-cache-clear-enabled="false"> (53、54行)的localderby修改为localmysql b)把段...
AWS Delegator允许账户管理员在班级预算的云资源中分配个人用户,协作组和学生。 AWSDelegator使用AWS Programmatic Billing Access维护有关用户和组及其对EC2和RDS实例的用法以及其成本的知识。 预算可以是用户/组...
可转发模块使用#def_delegator和#def_delegators方法将指定方法的委派给指定对象。 安装 将此行添加到您的应用程序的Gemfile : gem 'forwardable' 然后执行: $ bundle 或将其自己安装为: $ gem install ...
In proxy re-encryption (PRE), a semi-trusted proxy can transform a ciphertext under the delegator’s public key into another ciphertext that the delegatee can decrypt by his/her own private key....
提供了3种实现方式: 委托给ActiveRecord :: Relation(默认值) 委托给ActiveRecord :: Relation,并在两者之间链接ActiveRecord条件 扩展ActiveRecord :: Relation 用法 定义查询对象 class ArtistQuery < ...