2013-11-19 4 views
4

У меня есть следующий класс:Делегат атрибут методы родительского объекта

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.

Я хотел бы сделать это своего рода контейнер класса для вызова методов массива непосредственно, как

foo[i] 
foo.include? 

вместо явного доступа @alphabet:

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

Я знаю, что я мог бы определить много таких как

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

, но я ищу подходящий способ «наследуя» их.

ответ

9

Вы можете использовать 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 

Более подробную информацию о делегации шаблон дизайна в Руби here

+0

Ничего себе это приятно :) Есть ли шанс, что я могу делегировать ВСЕ методы? или, может быть, это не очень хорошая идея? –

+0

Не делегируйте все методы, поскольку они, вероятно, разделяют одни и те же имена методов, такие как 'class', что, вероятно, сделает хаос. – Zippie

+1

да, вы можете; Я обновил ответ – mdesantis

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 

Если у вас есть дополнительные вопросы, комментарии, и я обновлю ответ.

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, что, вероятно, приведет к хаосу.

Смежные вопросы