在库中包装第三方服务的最佳实践

时间:2011-04-25 15:31:01

标签: ruby-on-rails ruby api design-patterns

我有兴趣为我正在使用的第三方API编写库,我需要一些建议。在一次请求中,库的平均使用将涉及几个api调用。例如,一个api呼叫从第三方服务中抓取用户,然后另一个呼叫使用该用户抓取他/她的照片。每个API调用都将获得自己的库方法包装器以及处理错误/超时的额外逻辑,但我最大的问题是库是应该作为包含状态的单例还是作为一系列类方法。

例如:

user_id = ThirdParty.get_user("abc@gmail.com")
photos = ThirdParty.get_photos(user_id)

OR

thirdpartyservice = ThirdPartyService.new("abc@gmail.com")
photos = thirdpartyservice.get_photos

这些不一定是图书馆的确切设计,但我对每种方法的优点/缺点感到困惑。任何帮助都会很棒!

是的,我正在使用红宝石!

4 个答案:

答案 0 :(得分:4)

我希望库包含状态,因为这会降低用户端代码的复杂性(这就是API应该做的事情,增加简单性)。使用这种方法,用户不必跟踪user_id,因为库保持状态。

如果用户确实需要他们的user_id(或库存储的任何其他数据),您只需在库中创建attr_reader即可公开该数据。

要为get_photos方法添加fleixiblity,您可以执行以下操作:

class ThirdPartyService

  def get_photos(user_id=@id_stored_in_library)
    # do work
  end

end

这样它默认为存储的id,但是它增加了灵活性,因为用户可以选择用户ID。

答案 1 :(得分:2)

您需要基于该状态的状态(主机等)和行为,因此您应该使用对象,而不是一个单独的对象。

如上所述,您不应将get_photos等方法命名为photos

答案 2 :(得分:1)

我认为最佳做法是使用提供商和服务,而不是将其绑定到特定的服务提供商。您可能希望稍微抽象一下,而不是简单地包装库,默认情况下只允许单个服务提供者。

虽然在像Ruby这样的动态/鸭子类型语言中并不真正需要继承,但这可能有助于具体化“为什么”。

class MailProvider 

  def initialize(args); raise "Not Implemented"; end

  def send_mail(args); raise "Not Implemented"; end

end

class SendGridService < MailProvider

  def initialize(args)
    # Use args here
  end

  def send_mail(args)
    # Use SendGrid's API or Gem here
  end

end

然后,在您的代码中,您可以使用以下内容:

config.mail_provider = SendGridService.new({
  username: ENV['SENDGRID_USERNAME'],
  password: ENV['SENDGRID_PASSWORD']
})

config.mail_provider.send_mail({ subject: 'woot', message: 'Look ma, I did it!' })

然后六个月后,当您的用户喜欢您的Gem /库但想要MailChimp支持时,您只需创建第二个服务“MailChimpService”并允许您的客户使用新的提供商。

答案 3 :(得分:0)

使用getter和setter代替get_set_方法会更好。它是ruby标准(不确定,但是我在ruby代码中只看过一次get_和set_方法)。

如果您不需要在请求之间保存某些状态,请将其设置为静态。但这取决于很多因素。你能告诉我们你需要包装什么API吗?