2015-10-12 1 views
0

Я пишу класс, где переменная экземпляра имеет значение, которое должно быть установлено во время инициализации, или класс не будет работать. Класс предназначен для сильно подкласса, и я не хочу, чтобы все подклассы приходилось вызывать super в своих перегрузках initialize, поэтому я хотел бы установить переменную экземпляра до вызывается функция инициализации.Как сделать материал экземпляру класса до вызова #initialize?

Is возможно, путем перегрузки Class#new или что-то подобное, чтобы сделать некоторые пользовательские инициализации экземпляра перед тем функция initialize называется?

Как:

class Foo < MySuperClass 
    def initialize 
    # @foo is already set to 123 because of the magic in MySuperClass 
    assert @foo == 123 
    end 
end 
+1

Вы можете привести пример, объясняющий необходимость такого поведения? – xlembouras

+0

@xlembouras Я думал, что сделал это в вопросе. Суперкласс верхнего уровня определяет набор методов, которые все предполагают, что определенная переменная экземпляра была инициирована в методе '# initialize'. Если пользователь переопределяет '# initialize', тогда все эти методы ломаются. Я сделал это сам, и мне пришлось отлаживать какое-то время, прежде чем я понял, что переменная экземпляра была 'nil'. «Точка этого поведения» состоит в том, чтобы иметь возможность выполнить некоторую инициализацию * до того, как * будет выполняться подкласс, с добавленным бонусом, что подкласс не может случайно предотвратить перезапуск суперкласса, вызвав запутывающие ошибки. – Hubro

+0

@xlembouras Вот конкретный случай, о котором я говорю: https://github.com/Hubro/Datastruct. В нескольких методах предполагается, что '@ data' является определенным хешем и не будет выполнен, если он не инициализирован. – Hubro

ответ

3

Вы можете использовать шаблон метода шаблона вместо того, чтобы полагаться на #initialize в дочернем классе, как это:

class Parent 
    def initialize 
    @foo = 123 
    my_initialize 
    end 

    # Concrete classes must override 
    def my_initialize 
    raise NoMethodError 
    end 
end 

class Child < Parent 
    def my_initialize 
    puts @foo 
    end 
end 

Другой способ, если вы предпочитаете не использовать шаблонный метод, составляет prepend модуль, который содержит логику инициализации (prepend был добавлен в Ruby 2):

class Parent 
    def self.inherited(class_name) 
    class_name.prepend Initializer 
    end 

    module Initializer 
    def initialize 
     # Your initialization code 
     @foo = 123 

     # Call your child class's initialize 
     super 
    end 
    end 
end 

class Child < Parent 
    def initialize 
    puts @foo 
    end 
end 

Вот хороший учебник по модулю # препендом: http://gshutler.com/2013/04/ruby-2-module-prepend/

+0

Есть ли какие-либо преимущества в использовании метода preend-trick, используя метод в моем ответе? Лично я думаю, что использование 'Class # new' и' Class # allocate' * выглядит * более чистым и простым. – Hubro

+0

Нет технической проблемы для этого с Ruby. Мне лично не нравится возиться со встроенными методами. Это действительно взломать больше всего на свете, а 'Module # prepend' был добавлен на основной язык частично, чтобы облегчить необходимость таких хаков. – AmitA

0

Что о ленивая инициализация

class MySuperClass 
    protected 
    def foo 
    @foo ||= 123 
    end 
end 

class Foo < MySuperClass 
    def initialize 
    assert foo == 123 
    end 
end 
0

Я нашел интересный способ в this forum thread:

class MySuperClass 
    def self.new(*args, &block) 
    instance = allocate() 

    # Pre-initialize 
    instance.instance_variable_set(:@foo, 123) 

    instance.send(:initialize, *args, &block) 

    return instance 
    end 
end 

Это переопределяет метод по умолчанию Class.new и создает экземпляры вручную. Это означает, что я могу впрыгнуть и установить переменные экземпляра, прежде чем позвонить initialize.

+0

Hubro, я изменил свой ответ выше, чтобы включить «более чистый» способ. – AmitA

+0

После удаления '** kwargs' (который является непоследовательным и, возможно, ошибкой) и читается на' allocate', я убежден, что это самый чистый способ выполнить то, что я пытаюсь сделать. – Hubro

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