I have the following class:

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)

The central member here is @alphabet.

I'd like to make it some sort of a container class to invoke Array methods directly like

foo[i]
foo.include?

instead of explicitly accessing @alphabet:

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

I know I could define a lot of methods like

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

but I'm looking for a proper way of "inheriting" them.

有帮助吗?

解决方案

You can use Forwardable (it is included in the Ruby standard library):

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

If you wish to delegate all the methods not defined by your class you can use SimpleDelegator (also in the standard library); it lets you delegate all the methods that are not responded by the instance to an object specified by __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"

When the delegate doesn't need to be dynamic you can arrange the master class to be a subclass of DelegateClass, passing the name of the class to be delegated as argument and calling super passing the object to be delegated in the #initialize method of the master class:

class Alphabet < DelegateClass(Array)

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

More info about the delegation design pattern in Ruby here

其他提示

You could extend the Forwardable module:

class Alphabet
  require 'forwardable'
  extend Forwardable
  attr_accessor :alphabet

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


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

Then you can do:

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

Warning:

Don't try to delegate all methods (don't know if that's even possible) since they probably share some of the same method names such as class, which would probably make chaos.

In Ruby you can extend Objects like this.

class Array
  def second
    self[1]
  end
end

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

Or if you want to inherit an Array.

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

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

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

If you have any further questions, comment and I will update the answer.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top