重构 模块与类的使用规范

sennmac · 2015年12月16日 · 最后由 lgn21st 回复于 2015年12月16日 · 8506 次阅读

首先看一段 Ruby 代码

#定义
module A
  def self.a
    puts 'a'
  end
end
#使用
A.a

上面可以看到定义了一个 A 模块,然后在被使用时是通过 A 模块来调用 a 这个类方法。 但是我认为模块的从根本上是为了解决代码复用问题而存在,模块封装了一组被公用的常量/方法/变量,供其他模块/类进行引入。 在上面的代码中出现了两个不规范的地方 1.模块中不应该定义类方法,因为模块中的类方法永远无法被 include 成为目标类的类方法,这违反了模块设计的初衷。 2.如果希望某个方法以类方法的形式被调用到,使用 class 进行封装。 请大家给出意见。谢谢。么么哒。 -------update------- 原贴内容有点误人子弟了。 我原本以为 module 从设计上是为了

  1. mixin:提供一组方法/变量/常量以便混入其他模块/类。 2.namespace:提供命名空间。 除此之外对 module 的用法都是不规范的。 但是实际上 module 也可以做为 Utility 使用。 在官方的 Math 模块中,便提供了 Math.cos,Math.sqrt 等一系列的辅助方法。

请问从楼主的角度,能否给出不要把类方法定义在模块中的理由呢?

首先模块中允许定义类方法,且在很多场景下就应该这么干,例子到处都是(其实我觉得不应该叫类方法,而应该叫静态方法,或者 static method 感觉更合适,至少 ES6,Swift 都是这样叫的)

除了在模块中用 self.method_name 来定义之外,还有很多种办法达到同样的结果。

module A
  extend self
  def a
    puts 'a'
  end
end

还有

module A
  def a
    puts 'a'
  end
  module_function :a
end

#1 楼 @lgn21st 并不是说“能不能”,而是“规范不规范”。 模块的类方法注定了无法被目标类 inlucde 到。 可能我代码看的少,您能给出一些您看到的在模块中定义类方法的场景吗。

module A1
  def a
    puts 'a'
  end
  module_function :a
end

class B;include A1;end
#报错
B.a

module A2
  extend self
  def a
    puts 'a'
  end
end
class B;include A2;end
#报错
B.a

#2 楼 @sennmac module 中定义类方法是有用的,不是说无法被类 include 并当作类的类方法用,在 module 中定义类方法就不符合"规范"。

随便搜了一个例子,也许不能说明实际问题 https://gist.github.com/sshaw/53c27b148e903a07e494

#3 楼 @lgn21st THX。 下班之后我看看这段代码。 有问题的话,我还会提出来的,多谢您指教。

#4 楼 @sennmac 我随便搜了一个例子,是 module_function 的一个应用场景,刚刚仔细看了一下,发现这个例子不太合适。换个 Sinatra 中的 Delegator 的实现,主要是证明在 module 中定义类方法并不是没用,而是很有用,具体取决于场景。

https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1976

module 中定义类的一个作用就是作为类的 namespace

如果没有 namespace 那还不得天下大乱

#5 楼 @lgn21st 你给的例子还是在模块内自用了类方法,通过动态定义方法来缩减重复代码。 这个场景下,使用模块内的类方法是完全没用问题的,我自己曾经也写过这样的代码。 但是在我主题里面的场景下,应该是用 class 来封装这个方法,而不应该是用 module。 就这点,您有什么看法吗。谢谢指教。

#6 楼 @kikyous 没错,module 还有一个重要的用处就是 命名空间了。

#7 楼 @sennmac 我反复阅读你的主题,但是仍然认为你不是在描述一个限定的场景,而是通用场景,基于这个通用场景提出两个结论,在我看来不严谨,所以我选择了一个角度 模块的类方法不是没用的 来反驳。

如果换个角度,我会选择 模块不仅仅只是用来 Mixin 的,比如当我们想要求平方根的时候,我们可以用 Math.sqrt,这样的场景就是把类方法定义在模块中,当作 Utility 来用。

ruby 本来就有点反对规范的意思

module 的两个作用一个是 namespace,一个是 mixin,如果是 module with class method 可以理解为加了 namespace global function,如果没有 module 那不就是 global function 了吗 Math.sqrt

倘若你 program 和内部的 instance variable 无关,无须实例化,我觉得没必要放在 class 里,当然放在 class 也没有问题 class 就是继承 module 的,放 class 就等于放在 module 里了,其次 java 就是通过把 class method 封装在 class 里的来实现类似的裸 function 机制,当然 java 是没有 module 的概念的

#9 楼 @lgn21st Math.sqrt 是一个极好的打我脸的例子.. 看样子一直以来,是我对 Utility 的定义过于沉重了.. 多谢,多谢。 @cunheise 我个人开始比较反对这种 namespace global function.. 总觉得通过模块 (一组方法/变量/常量的集合体,非常的轻) 上调用方法极其的别扭.. 后来转念一想..模块不也就是个实例 (Module 类的) 吗.. 顿时就释然了..

#10 楼 @lithium4010 我的理解是保证代码可读性和概念一致性上,需要一些规范。

#12 楼 @sennmac 不要上升到打脸嘛,讨论技术本来就是一件很愉快的事情,跟你讨论的过程也是我梳理概念,查找资料和学习的过程。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号