加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Ruby mixins正在寻找最佳实践

发布时间:2020-12-17 02:56:34 所属栏目:百科 来源:网络整理
导读:我正在编写 Ruby Gem,我有法拉第配置的连接模块 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 re
我正在编写 Ruby Gem,我有法拉第配置的连接模块

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

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

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

我使用Request的Class Cusomer看起来像这样:

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 class方法我正在调用Request#get方法,因为我在Customer中扩展了Request.然后我在Request模块中使用self.extended方法在Customer类中使Request#可用,所以我有一个问题就是使用这样的mixins这个好方法,或者你有什么建议吗?

解决方法

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

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

编写mixins的一个指导原则是使所有内容都以“-able”结尾,因此你将拥有Enumerable,Sortable,Runnable,Callable等.从这个意义上说,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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读