新手问题 如何避免代码的重复引入?

redemption · April 16, 2016 · Last by luikore replied at April 17, 2016 · 3093 hits

背景

我遇到这么一个场景。比如我有两个 module(Searchable, Filterable)和一个使用这两个 module 的类 A。 定义如下:

module Searchable
end

module Filterable
end

class A
  include Searchable
  include Filterable
end

但是在 Searchable、Filterable、A 中,他们里面包含的 method 很多都会用到一组公共的 methods,为了避免代码重复,我的一个想法是讲这些公共方法也作为 module 提取出来(例如叫做 Pub)。那么上面的代码就会变成下面这样:

module Pub
end

module Searchable
  include Pub
end

module Filterable
  include Pub
end

class A
  include Pub
  include Searchable
  include Filterable
end

上面我之所以 3 个地方我都 include 了 Pub 是因为: 1、Searchable 和 Filterable 两个 module 可能被其他地方重复使用,所以为了保证代码能够运行,所以需要包含。 2、在 class A 中 include Pub 主要是设想一种情况,就是我并不知道 Searchable 这些 module 中已经 include Pub,但是我需要用到 Pub 的功能,所以我 include 了 Pub (当然,如果完全是自己写的代码肯定不会出现这种情况)

问题: 所以在上面的代码结构中,我们其实是在 class A 中多次 include 了 Pub 这个 module,虽然看到相关资料说 include 并不会实际去 copy 代码过来,只会产生一个指向 Pub module 的 reference,但是这样毕竟还是重复引用了。

我的疑问

  1. 上面我的这种解决代码重复方法(就是将公用方法放入一个 module 中去)是否正确?如果不正确,更好的应该是什么方式
  2. 在不考虑我用这种方式解决代码复用是否正确的前提下。单纯考虑上面那种代码结构,对于这种重复引入应该怎么解决?
  3. 各位能否给提供些学习资料(我 google 了半天真没找到,可能关键字没用对,英文不是很好),主要就是代码相互引用这些方面的。除了这里的 module,我对于代码文件之间如何 require,才不会造成重复 require 同一个文件的问题也不是非常明白。

我是来消除零回复的......

方法没错,而且重复引入不是问题,不用担心

➜  cat a.rb                            
module Pub
end

module Searchable
  include Pub
end

module Filterable
  include Pub
end

class A
  include Searchable
  include Filterable
end
➜  ruby -r ./a.rb -e 'puts A.ancestors'
A
Filterable
Searchable
Pub
Object
Kernel
BasicObject

➜  cat a.rb                            
module Pub
end

module Searchable
  include Pub
end

module Filterable
  include Pub
end

class A
  include Pub
  include Searchable
  include Filterable
end
➜  ruby -r ./a.rb -e 'puts A.ancestors'
A
Filterable
Searchable
Pub
Object
Kernel
BasicObject

可见,那行 include 加了也不会带来什么负担

module 可以做菱形继承,但是没有 C++ 和 Python 里菱形继承的问题

在调用 module 中的方法的时候,当第一次找到了该方法,后面的 module 相同的方法是不会再被重复调用的吧。

module Searchable
  included do |klass|
     klass.send(:incude, Pub) unless klass.ancestors.include? Pub
  end
end

这样 ok 吗?

5 Floor has deleted

@fsword @luikore @pathbox module 不存在重复引用的问题话,那么哪些情况是需要注意的呢?有没有这方面系统一点的资料呢?关于文件的 require 有没有这个问题呢?

很同意五楼的看法(不知道为啥要删除掉 =/=),楼主这个例子用法没有问题,但是做法可能并不合适。从背景来看,在 Pub 模块中存在若干公开的方法,当 Searchable 中 include 了 Pub 模块,实际会将这些方法作为自己对外公开的一部分。那么想要对 Searchable 的进行 include 的类型,也就是下文的 A 应当会确切的知晓这部分方法,而非再一次的 include Pub。如果 Searchable 的开发者并不希望这部分对外公开,应当对此细节进行屏蔽;当然如果你说这样做对于使用 Searchable 的使用者来说,即使不知道这点,然后再次 include Pub 也没事,那么只能祝你好运了。

关于 require,如果一个文件已经 require 了,那么再次 require 是不会真正执行的,你可以在终端试下,这个方法会返回一个布尔值的,代表是否真正载入。如果需要重复载入,那么使用 load 方法。结论是,require 也没有这个问题。

#6 楼 @redemption https://ruby-china.org/topics/21304 这篇帖子。require 问题 上楼的答的清晰了

你可以参考 ActiveSupport::Concern 的实现

#2 楼 @luikore 能说的更多一点不?我没真正接触过多重继承的情境,理论上也比较贫乏,说不了更多了

假设 B 和 C 继承 A, D 继承 B 和 C

  A
 / \
B   C
 \ /
  D

在 Python 中,如果 B 带实例变量 a, C 也带实例变量 a, 那么 D.a 应该算哪个?使用者需要记住语言规范的定义才知道,而且如何操作另一个也是麻烦的事情。

Ruby 的实例变量不区分是哪个 module 或者 class 设置的,只要名字一样就是同一个。

You need to Sign in before reply, if you don't have an account, please Sign up first.