扩展类实例方法的返回值

时间:2016-05-25 08:14:47

标签: ruby class instance-methods

我有一个具有实例方法的类,它返回一个哈希值。我无法直接更改该类的代码,但我可以使用模块扩展它。我需要在方法的返回哈希中添加一些新键。像这样:

class Processor
  def process
    { a: 1 }
  end
end

module ProcessorCustom
  def process
    super.merge(b: 2) # Not works :(
  end
end

Processor.send :include, ProcessorCustom

processor = Processor.new
processor.process  # returns { a: 1 }, not { a: 1, b: 2 }

我该怎么做?感谢。

3 个答案:

答案 0 :(得分:5)

您可以拨打prepend而不是include

Processor.prepend(ProcessorCustom)

processor = Processor.new
processor.process
#=> {:a=>1, :b=>2}

prependinclude导致不同的祖先顺序:

module A; end
module B; end
module C; end

B.ancestors     #=> [B]

B.include(C)
B.ancestors     #=> [B, C]

B.prepend(A)
B.ancestors     #=> [A, B, C]

替代

根据您的使用情况,您还可以extend特定实例:(这不会影响其他实例)

processor = Processor.new
processor.extend(ProcessorCustom)
processor.process
#=> {:a=>1, :b=>2}

或使用SimpleDelegator来实施decorator pattern

require 'delegate'
class ProcessorCustom < SimpleDelegator
  def process
    super.merge(b: 2)
  end
end

processor = ProcessorCustom.new(Processor.new)
processor.process  #=> {:a=>1, :b=>2}

答案 1 :(得分:2)

我认为第一个要考虑的选择是需要读者理解最少的工作;以及面向对象的软件,它将成为专门研究超类行为的子类。如果,只有当有令人信服的理由这样做时,我才会偏离这一点。

这个怎么样?:

#!/usr/bin/env ruby

class Processor
  def foo
    { x: 3 }
  end
end


class MyProcessor < Processor
  def foo
    super.merge({ y: 7 })
  end
end

p MyProcessor.new.foo  # outputs: {:x=>3, :y=>7}

答案 2 :(得分:1)

我认为创建代理比污染原始类更好。

class Proxy
  def initialize(target)
    @target = target
  end

  # add some syntactic sugar
  singleton_class.class_eval { alias [] new }

  def process
    @target.process.merge!(b: 2)
  end
end

Proxy[Processor.new].process  #=> {a: 1, b: 2}

您甚至可以创建自己的动态代理。

class DynamicProxy < BasicObject
  def initialize(target)
    @target = target
  end

  # again, add some syntactic sugar
  singleton_class.class_eval { alias [] new }

  def method_missing(name, *args, &block)
    super unless @target.respond_to?(name)
    # Do something before calling the target method
    result = @target.send(name, *args, &block)
    # Do something after calling the target method
  end

  def respond_to_missing?(name, include_private = false)
    @target.respond_to?(name, include_private)
  end
end

要隐藏创建Processor实例的详细信息,您可以创建一个简单的工厂来处理创建。

module ProcessorFactory
  def self.create
    DynamicProxy[Processor.new]
  end
end

然后你可以完成你的工作

ProcessorFactory.create.process