子类无需重定义方法更新第三方类依赖的技术问询
解决方案:动态继承第三方类并实现实例级id重写
问题1:能否向FooChild传递:id的引用让子类Baz重定义?
可以。核心问题是原实现中FooChild的id方法硬绑定到了Bar.id,导致Baz实例调用时仍使用父类的id值。解决思路是让动态生成的Foo子类的id方法动态关联当前客户端实例的id,而非固定绑定到Bar类。
修改后的示例代码:
# 第三方类Foo(不可修改) class Foo def self.id 'foo' end def identify "Referring to '#{self.class.id}'" end end class Bar def self.embed define_method :foo do # 保存当前客户端实例(Bar/Baz实例)的引用 current_client = self # 动态创建Foo子类,将其id方法绑定到当前实例的id FooChild = Class.new(Foo) do singleton_class.define_method(:id) do current_client.id end end FooChild.new end end def id 'bar' end embed end class Baz < Bar def id 'baz' end end # 测试输出:Referring to 'baz' puts Baz.new.foo.identify
这里的关键是在生成foo方法时,将当前客户端实例(self)保存下来,让FooChild的类方法id调用该实例的id方法。当Baz重写id后,FooChild会自动使用Baz实例的id值,完全不需要重写foo方法。
问题2:更简洁高效的替代实现方法
推荐两种更轻量的方案,避免动态创建子类的开销:
方案1:装饰器模式(委托)
通过包装Foo实例,拦截identify方法并替换id值,无需修改原Foo类或创建子类:
class FooDecorator def initialize(foo_instance, client) @foo = foo_instance @client = client end # 重写identify方法,替换id值 def identify original_text = @foo.identify original_text.gsub(/'[^']+'/, "'#{@client.id}'") end # 委托其他所有方法给原Foo实例 def method_missing(name, *args, &block) @foo.send(name, *args, &block) if @foo.respond_to?(name) end def respond_to_missing?(name, include_private = false) @foo.respond_to?(name, include_private) || super end end class Bar def foo FooDecorator.new(Foo.new, self) end def id 'bar' end end class Baz < Bar def id 'baz' end end # 测试输出:Referring to 'baz' puts Baz.new.foo.identify
这种方式更灵活,不会产生额外的类,性能开销更低,也更容易维护。
方案2:简化版动态子类绑定
如果坚持用动态子类的方式,可以用proc绑定简化代码:
class Bar def self.embed define_method :foo do Class.new(Foo) do singleton_class.define_method(:id, &proc { self.id }.bind(self)) end.new end end def id 'bar' end embed end class Baz < Bar def id 'baz' end end
通过proc { self.id }.bind(self)直接把客户端实例的id方法绑定到FooChild的类方法id上,代码更紧凑。
内容的提问来源于stack exchange,提问作者rellampec
相关产品推荐
相关产品推荐

