Ruby:改进复杂的初始化方法

时间:2015-08-16 19:04:41

标签: ruby refactoring

我有一段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

但是,我需要这个代码是线程安全的,所以我不能在这里使用该模式。

编辑:我对此代码的主要关注是,我希望看到build_a应在初始化后调用,在build_a而不是{{1}的定义中编写的事实方法。

2 个答案:

答案 0 :(得分:1)

现在我一起去了

#bg {
  background-image: url('small.jpg');
}

@media (min-width: 768px) {
  #bg {
    background-image: url('big.jpg');
  }
}

我不是100%确定我喜欢这个解决方案,但它确实有效。

编辑:经过一番思考和玩弄Piotr Kruczek的解决方案,我接受了这个:

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

答案 1 :(得分: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,我认为这是最好的方法。