"扩展自我"是否是实用模块的反模式?

m1f*_*ley 13 ruby

Steve Klabnik最近在一个实用程序模块的拉取请求中说:

[代码]模糊了这些是类方法的事实,我们希望以这种方式使用它们.另外,我认为这extend self通常是一种反模式,除非在某些情况下,否则不应该使用.我想到了,我认为这是其中一个案例.

  • 在创建实用程序模块(例如,用于数学)时,声明方法的最佳方法是什么?
  • 什么时候extend self成语适合?

Rus*_*ell 24

他没有说1)为什么他认为这是一种反模式(除了模糊你定义类方法的事实)或2)为什么,考虑到它,这是其中一个案例并不完全有用他认为不应该使用它,因此很难专门反驳他的任何论点.

但是,我不相信这extend self 一种反模式.实用程序模块似乎是一个用例的好例子.我也用它作为测试夹具的简易存储.

我认为值得研究一下extend self,它可能存在哪些问题,以及有哪些替代方案.

在它的核心,它只是一种避免必须self.在模块中的每个方法定义之前编写的方法,你永远不打算混合到一个类中,因此永远不会创建自己的"实例",因此只能定义有'类'方法(如果你想能够调用它们,那就是).

它是否掩盖了您希望将方法用作类方法的事实?嗯,是的,如果你不看文件的顶部到它所说的位置extend self,那是可能的.但是,我认为,如果你有可能让这种混乱,你的课程可能太复杂了.

从你的班级 - 从名称和内容 - 中可以明显看出它是作为效用函数的集合.理想情况下,无论如何它都不会超过屏幕高度,所以extend self几乎永远不会出现在视线之外.正如我们所看到的,替代方案也会遇到几乎完全相同的问题.

一种替代方法是使用class << self这样的:

module Utility
  class << self
    def utility_function1
    end
    def utility_function2
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我不是这个的粉丝,尤其是因为它引入了额外的缩进层.它也很难看(完全主观,我知道).它也会遇到完全相同的"模糊"你正在定义类方法这一事实的问题.

使用这种方法你也可以自由地定义class << self块外的实例方法- 这可能会导致这样做的诱惑(虽然我希望它不会),所以我认为extend self在这方面是优越的通过消除这种混乱的水的可能性.

(当然,"长手"使用方式也是如此def self.utility_function.)

另一种方法可能是使用单例对象.我认为这根本不是一个好主意,因为单个对象是一个理由的对象 - 它意味着保持状态和做事,但也是唯一存在的对象.这对于实用程序模块来说根本没有意义,它应该是一系列独立的无状态函数.您不希望MathUtils.cos(90)根据内部状态返回不同的值MathUtils,对吧?(我知道你当然可以在模块中保存状态并完成所有这些事情,但它对我来说更像是一个语义划分而不是技术划分).

它也导致同样的问题,即可以说是模糊了这些方法被称为类方法(类型)的事实.它们被定义为实例方法,您将它们称为实例方法,但首先通过调用类方法获取类的单个实例instance.

class MathSingleton
  include Singleton

  def cos x
  end
end

MathSingleton.instance.cos x
Run Code Online (Sandbox Code Playgroud)

extend self对于这个目的,这将是一个可怕的替代方案.但是,看起来,唯一的事情表明这些方法将被用作单例实例上的方法是一行只是在顶部,就像extend self.

那么还有其他可能的缺点呢?我不知道有什么,但如果有其他人这样做,我会有兴趣听到它们.

我认为这extend self会导致更短的代码,从而省去了无关self.的代码,并允许您专注于名称及其方法的含义.

它还具有很好的属性,例如,如果您正在编写另一个使用大量实用程序功能的类,您可以将其混合使用,并且每次都可以使用它们而无需使用模块名称.就像静态导入在其他语言中一样.