Ruby mixins正在寻找最佳实践

时间:2016-07-20 10:25:55

标签: ruby mixins

我正在编写Ruby Gem,我

before_save :set_status, if: 'payload[:current_user_id] && payload[:current_user_id] != 1'

def set_status
    status = 2
end
配置before_save :set_status, if: 'payload[:current_user_id] && payload[:current_user_id] != 1' def set_status status = 2 end 模块

发出API请求的第二个模块如下所示:

Connection

我使用Faraday的{​​{1}}类看起来像这样:

module Example
  module Connection
    private

    def connection
      Faraday.new(url: 'http://localhost:3000/api') do |conn|
        conn.request  :url_encoded             # form-encode POST params
        conn.response :logger                  # log requests to STDOUT
        conn.adapter  Faraday.default_adapter  # make requests with Net::HTTP
        conn.use      Faraday::Response::ParseJson
        conn.use      FaradayMiddleware::RaiseHttpException
      end
    end
  end
end

所以,您在module Example module Request include Connection def get(uri) connection.get(uri).body end def post(url, attributes) response = connection.post(url) do |request| request.body = attributes.to_json end end def self.extended(base) base.include(InstanceMethods) end module InstanceMethods include Connection def put(url, attributes) response = connection.put(url) do |request| request.body = attributes.to_json end end end end end 类方法中看到我调用了Cusomer方法,因为我在Request中扩展了module Example class Customer extend Request attr_accessor :id, :name, :age def initialize(attrs) attrs.each do |key, value| instance_variable_set("@#{key}", value) end end def self.all customers = get('v1/customer') customers.map { |cust| new cust } end def save params = { id: self.id, age: self.age name: self.name, } put("v1/customers/#{self.id}", params) end end end 。然后我在Customer#all模块中使用self.extended方法在Request#get类中使Request可用,所以我有一个问题就是使用这样的mixins这个好方法,或者你有什么建议吗?

1 个答案:

答案 0 :(得分:5)

Mixins是一个奇怪的野兽。最佳做法取决于您与谁交谈。就重复使用而言,你已经通过mixins实现了这一点,你可以很好地分离关注点。

但是,mixins是一种继承形式(你可以看一下#ancestors)。我会挑战你说你不应该在这里使用继承,因为CustomerConnection没有“is-a”关系。我建议你改用合成(例如传入Connection/Request),因为在这种情况下对我来说更有意义并且具有更强的封装。

编写mixins的一个指导原则是让所有内容都以“-able”结尾,因此您将拥有EnumerableSortableRunnableCallable等。从这个意义上说,mixins是一般的扩展,提供某种依赖于非常特定的接口的帮助(例如Enumerable依赖于实现#each的类。)

您也可以使用mixins来解决问题。例如,我们过去在后台作业中使用了mixins,因此我们可以添加日志记录,而无需触及类的源代码。在这种情况下,如果一个新工作想要记录,那么他们只是混合了与框架耦合的关注点,并将正确注入。

我的一般经验法则是,如果您不需要,请不要使用它们。在大多数情况下,他们会使代码理解得更复杂

编辑:添加一个组合示例。为了维护上面的接口,你需要有某种全局连接状态,所以它可能没有意义。这是使用合成

的替代方案
class CustomerConnection
  # CustomerConnection is composed of a Connection and retains isolation
  # of responsibilities. It also uses constructor injection (e.g. takes
  # its dependencies in the constructor) which means easy testing.
  def initialize(connection)
    @connection = connection
  end

  def all_customers
    @connection.get('v1/customers').map { |res| Customer.new(res) }
  end
end

connection = Connection.new
CustomerConnection.new(connection).all_customers