正确实现依赖注入

时间:2014-11-05 10:03:41

标签: ruby-on-rails ruby

我在实施此服务的最佳方式之间徘徊:

目前我有这个:

class ReissueInvoices

  def initialize(registration, invoice_gateway, invoice_generator)
    @registration = registration
    @invoice_gateway = invoice_gateway
    @invoice_generator = invoice_generator
  end

  def call
    void_current_invoices
    regenerate_invoices
    create_third_party_invoices
  end

  private

  attr_reader :registration, :invoice_gateway, :invoice_generator

  def void_current_invoices
    registration.invoices.each do |invoice|
      unless invoice.paid?
        invoice_gateway.void_invoice(invoice)
      end
    end
  end

  def regenerate_invoices
    invoice_generator.call(registration)
  end

  def create_third_party_invoices
    invoice_gateway.create_invoices(registration)
  end

end

我这样称呼(通常来自我的控制器):

ReissueInvoices.new(@registration, InvoiceGateway.new, InvoiceGenerator.new).call

我显然有一个InvoiceGateway,InvoiceRegistration类,并将这些作为依赖项传递给我的ReissueInvoices类。

这是最好的做事方式吗?这是否正确实现依赖注入?或者我应该将我的ReissueInvoices类更改为类似这样的内容从initialize方法中删除参数并添加私有方法来创建和访问invoice_generator和invoice_gateway对象:

class ReissueInvoices

  def initialize(registration)
    @registration = registration
  end

  def call
    void_current_invoices
    regenerate_invoices
    create_third_party_invoices
  end

  private

  attr_reader :registration

  def invoice_gateway
    @invoice_gateway ||= InvoiceGateway.new
  end

  def invoice_generator
    @invoice_generator ||= InvoiceGenerator.new
  end

.....

并称之为

ReissueInvoices.new(@registration).call

最后,你们怎么看待在初始化器中定义这样的默认参数:

def initialize(registration, invoice_gateway=InvoiceGateway.new, invoice_generator=InvoiceGenerator.new)
   @registration = registration
   @invoice_gateway = invoice_gateway
   @invoice_generator = invoice_generator
 end

好还是坏?

谢谢,

马特

1 个答案:

答案 0 :(得分:1)

为什么您的网关不需要凭据等内容?我猜它确实如此,网关的凭证只是硬编码。真的,这应该在某处初始化然后传入(因为你的ReissueInvoices类不应该处理如何设置网关,可能只有一个网关,你应该在任何地方使用同一个对象)

我不会在发票生成器中烦恼,除非它很昂贵,或者你需要控制它为测试返回的内容,或者有多种方法来生成这个东西,你可以&# 39;从这里合理地知道哪个是正确的。这只是因为它是一个做一些计算并返回一个值的东西。除了我提到的那些考虑因素外,简单明了。

另外,我可能会将其更改为:

module ReissueInvoices
  def self.call(registration, invoice_gateway)
    registration.invoices.each do |invoice|
      invoice_gateway.void_invoice(invoice) unless invoice.paid?
    end
    InvoiceGenerator.call(registration)
    invoice_gateway.create_invoices(registration)
  end
end

思维过程大致如下:

  • 我永远不会去看那个调用方法而不需要查看他们实现的方法,所以让我们看看他们做了什么。哦,他们只是隐藏了合作者。
  • 将它们拉到#call
  • "为什么要实例化InvoiceGenerator?它不需要参数,所以它不应该有状态,所以不需要实例化。我假设它只有一个方法,因为该方法名为#call,所以只需使它成为单例方法,如果有一些值来实例化,单例方法就可以做出这个决定。
  • 现在一切都在ReissueInvoices#call,还剩下什么?它只是用于支持此操作的对象性(#initialize和访问者)。相反,将其设为名为call的类方法。然后,这些变成了当地的变种。
  • 既然我们只有一个类方法,那么这个类就会产生误导,因为它不会被实例化。因此,请将其改为模块。