如何在定义ClassName.create和ClassName.create时重用代码?

时间:2011-11-17 07:29:51

标签: ruby

假设我出于某种原因包装了ActiveRecord类,并且我已经定义了create类方法。

class BusCreator
  def self.create(attr)
    bus = Bus.new(attr)
    bus.departure_time = bus.arrival_time - bus.trip_duration
    bus.save
    bus
  end
end

create!上定义BusCreator方法的最佳方法是什么?显然,它应该执行与BusCreator.create相同的逻辑,但应该调用save!而不是save

4 个答案:

答案 0 :(得分:1)

假设你有比上面一行更多的初始化逻辑,并且由于某种原因你不能把它放在AR模型本身,你可能会考虑以下几点:

class BusCreator
  def self.create(attr)
    bus = initialize(attr)
    bus.save
    bus
  end

  def self.create!(attr)
    bus = initialize(attr)
    bus.save!
    bus
  end

private

  def self.initialize(attr)
    bus = Bus.new(attr)
    bus.departure_time = bus.arrival_time - bus.trip_duration
    # ...more logic here
    bus
  end
end

答案 1 :(得分:0)

你可以这样做:

class BusCreator
  def self.create(attr)
    real_create(attr, :save)
  end
  def self.create!(attr)
    real_create(attr, :save!)
  end

  #...

  private

  def self.real_create(attr, save_using)
    bus = Bus.new(attr)
    bus.departure_time = bus.arrival_time - bus.trip_duration
    bus.send(save_using)
    bus
  end
end

答案 2 :(得分:0)

最佳答案在某种程度上取决于您希望create!做什么。如果您希望它与create执行相同的操作,但在失败时引发异常,则可以执行以下操作:

class BusCreator
  def self.create(attr)
    bus = build_bus(attr)
    bus.save
    bus
  end

  def self.create!(attr)
    bus = create(attr)
    if bus.new_record? # check if save succeeded
      raise MyException, 'creation failed!'
    end
    bus
  end
end

class MyException < StandardError; end

否则,这只是将方法的常用元素提取到另一个充当帮助者的方法中的问题:

class BusCreator
  def self.create(attr)
    bus = build_bus(attr)
    bus.save
    bus
  end

  def self.create!(attr)
    bus = build_bus(attr)
    bus.save!
    bus
  end

  private  

  def self.build_bus(attr)
    bus = Bus.new(attr)
    bus.departure_time = bus.arrival_time - bus.trip_duration
  end
end

这里有很多样式选项,如果你真的想减少重复,你可以做这样的事情:

class BusCreator
  def self.create(attr)
    bus = build_bus(attr)
  end

  def self.create!(attr)
    bus = build_bus(attr, true)
  end

private  

  def self.build_bus(attr, big_save=false)
    bus = Bus.new(attr)
    bus.departure_time = bus.arrival_time - bus.trip_duration
    big_save ? bus.save! : bus.save
    bus
  end
end

答案 3 :(得分:0)

让我们看看你给出的例子。特别是这一行:

bus.departure_time = bus.arrival_time - bus.trip_duration

由于departure_time取决于arrival_timetrip_duration,而Bus又是同一对象的属性,因此没有理由将此代码放在Bus之外。这意味着,您只需将其包装在方法中,然后调用该方法。

如果我们认为这是一个人为的例子,并且实际上你想做的事情比这更复杂,那么你真正需要的是new类除了class Bus # ... def my_complex_method self.departure_time = arrival_time - trip_duration # some other complex stuff # ... end # ... # We are doing it here because BusCreator does not and should not # know stuff internal to Bus def self.new_with_departure_time(attr, other_optional_stuff) bus = new(attr) bus.my_complex_method bus.process(other_optional_stuff) bus end end 以外的新工厂。所以你用这样的工厂方法包装它:

save

请务必注意,此处的抽象与save!Bus#new无关,但Bus#newmy_complex_method后跟save。这是因为save!class BusCreator def self.create(attr) bus = Bus.new_with_departure_time(attr, other_stuff) bus.save bus end def self.create!(attr) bus = Bus.new_with_departure_time(attr, other_stuff) bus.save! bus end end 表示不同的行为,因此具有与之关联的不同规格。所以使用这些2的代码实际上不应该是抽象的。

现在,回到我们之前定义的工厂,你会像这样使用它:

{{1}}