Ruby mixins正在寻找最佳实践
我正在编写
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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |