将属性方法委托给父对象

时间:2013-11-19 13:20:37

标签: ruby delegation

我有以下课程:

class Alphabet

  attr_reader :letter_freqs, :statistic_letter

  def initialize(lang)
    @lang = lang
    case lang
    when :en
      @alphabet = ('A'..'Z').to_a
      @letter_freqs = { ... }
    when :ru
      @alphabet = ('А'..'Я').to_a.insert(6, 'Ё')
      @letter_freqs = { ... }
    ...
    end
    @statistic_letter = @letter_freqs.max_by { |k, v| v }[0]
  end

end

foo = Alphabet.new(:en)

此处的中心成员是@alphabet

我想让它成为一种容器类来直接调用Array方法,比如

foo[i]
foo.include?

而非明确访问@alphabet

foo.alphabet[i]
foo.alphabet.include?  

我知道我可以定义很多方法,比如

def [](i)
  @alphabet[i]
end

但我正在寻找一种“继承”它们的正确方法。

3 个答案:

答案 0 :(得分:10)

您可以使用Forwardable(它包含在Ruby标准库中):

require 'forwardable'

class Alphabet

  extend Forwardable
  def_delegators :@alphabet, :[], :include?

  def initialize
    @alphabet = ('A'..'Z').to_a
  end

end

foo = Alphabet.new

p foo[0]           #=> "A"
p foo.include? 'ç' #=> false

如果您希望委派您的课程未定义的所有方法,您可以使用SimpleDelegator(也在标准库中);它允许您将实例未响应的所有方法委托给__setobj__指定的对象:

require 'delegate'

class Alphabet < SimpleDelegator

  def initialize
    @alphabet = ('A'..'Z').to_a
    __setobj__(@alphabet)
  end

  def index
    'This is not @alphabet.index'
  end

end

foo = Alphabet.new

p foo[0]           #=> "A"
p foo.include? 'ç' #=> false
p foo.index        #=> "This is not @alphabet.index"

当委托不需要是动态的时,您可以将主类安排为DelegateClass的子类,传递要作为参数委派的类的名称并调用super传递要在主类的#initialize方法中委派的对象:

class Alphabet < DelegateClass(Array)

  def initialize
    @alphabet = ('A'..'Z').to_a
    super(@alphabet)
  end

有关Ruby here

中的委托设计模式的更多信息

答案 1 :(得分:2)

您可以扩展Forwardable模块:

class Alphabet
  require 'forwardable'
  extend Forwardable
  attr_accessor :alphabet

  def initialize
    @alphabet = [1,2,3]
  end


  def_delegator :@alphabet, :[], :include?
end

然后你可以这样做:

alpha = Alphabet.new
alpha[1]==hey.alphabet[1]
=> true

警告:

不要尝试委托所有方法(不知道是否可能),因为它们可能共享一些相同的方法名称,例如class,这可能会造成混乱。

答案 2 :(得分:0)

在Ruby中,您可以像这样扩展对象。

class Array
  def second
    self[1]
  end
end

[1, 2, 3, 4, 5].second
# => 2

或者如果你想继承一个数组。

class Foo < Array
  def second
    self[1]
  end
end

[1, 2, 3, 4, 5].include? 2
# => true

[1, 2, 3, 4, 5].second
# => 2

如果您有任何其他问题,请发表评论,我会更新答案。