2015-08-16 3 views
3

У меня есть кусок кода Ruby, который сводится к следующему:Ruby: Совершенствование метода сложной инициализации

class Foo 
    attr_reader :a, :b, :c 
    def initialize 
    build_a 
    build_b 
    build_c 
    end 

    private 

    def build_a 
    # something complex that eventually results in @a = something 
    end 
    def build_b 
    # something complex that eventually results in @b = something 
    end 
    def build_c 
    # something complex that eventually results in @c = something 
    end 
end 

Вызов build_* в методе initialize кажется немного излишним. Есть ли лучший способ написать это? Конечно, я в курсе ленивым загружения:

class A 
    def a 
    @a ||= something_complex 
    end 
end 

Но, мне нужен этот код, чтобы быть поточно, поэтому я не могу использовать этот шаблон здесь.

Edit: Моя главная забота с этим кодом, в том, что я хотел бы видеть тот факт, что build_a должен быть вызван после инициализации, написанный в определении build_a, а не в методе initialize.

ответ

1

Пока я пошел с

require 'active_support/callbacks' 

class Foo 
    include ActiveSupport::Callbacks 
    define_callbacks :initialize 

    attr_reader :a, :b, :c 

    def initialize 
    run_callbacks :initialize do 
     # do the rest of initialize 
    end 
    end 

    protected 

    def build_a 
    # something complex that eventually results in @a = something 
    end 
    set_callback :initialize, :after, :build_a 

    def build_b 
    # something complex that eventually results in @b = something 
    end 
    set_callback :initialize, :after, :build_b 

    def build_c 
    # something complex that eventually results in @c = something 
    end 
    set_callback :initialize, :after, :build_c 
end 

Я не 100% уверен, что мне нравится это решение, но это работает.

Edit: После некоторого мышления и играя с решением Петра Kruczek, я пошел с этим:

class Foo 
    def initialize 
    protected_methods.grep(/^initialize_/).each do |method| 
     send(method) 
    end 
    end 

    protected 

    def initialize_a 
    # something complex that eventually results in @a = something 
    end 

    def initialize_b 
    # something complex that eventually results in @b = something 
    end 

    def initialize_c 
    # something complex that eventually results in @c = something 
    end 
end 
1

Эти обратные вызовы будут реальная боль в заднице, чтобы проверить и сохранить. не это решение было бы лучше ?:

class Foo 
    attr_reader :a, :b, :c 

    def initialize 
    # things that belong in initialize 
    end 

    def self.call # or any other name 
    new.build_things 
    end 

    def build_things 
    build_a 
    build_b 
    build_c 
    end 
end 

Единственный недостаток заключается в том, что вы будете использовать Foo.call вместо Foo.new. Если вы не хотите, чтобы этот класс «чувствовал» себя как объект службы, я бы пошел и обернул его одним, например, FooBuilder. Таким образом, вы избегаете обратных вызовов, тестирование легко и ваш код чистый и читаемый. Я думаю, что это лучший подход, если вы хотите выполнить build_things после инициализации.

+0

Хм, мне нравится идея помещать этот список строителей в другой метод, но я не думаю, что он решает главную проблему: этот список вызываемых методов. Возможно, я попытаюсь реализовать цикл, который вызывает все методы 'build_ *' или что-то в этом роде. –

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