这段代码实际上并没有定义类方法:
module SomeService
def test_code
"abc"
end
def self.test_code_2
"xyz"
end
end
它在模块本身上声明了一个方法,您可以通过运行SomeService.test_code_2 来验证它。那是因为self 不像其他语言中的static 那样是“类方法关键字”——它只是对当前词法范围的引用。在这种情况下是模块本身。
在类中声明方法时:
class Foo
def self.bar
"Hello world"
end
end
self 是所谓的单例类 - Class 类的一个实例。所以它在Foo 类上定义了一个方法。
当您include 类中的模块时,您将该模块添加到该类的祖先链中。因此,它可以调用模块的 instance 方法,就好像它在它自己的位置一样。您可以将此与 extend 进行对比,后者将模块的方法导入类(test_code 成为类方法)。
因此ClassMethods 模式使用内部模块when its included 扩展类:
module SomeService
def self.included(base)
base.extend ClassMethods
end
def test_code
"abc"
end
module ClassMethods
def test_code_2
"xyz"
end
end
end
如何将这样的功能从模型中移出,而不是移到关注点中?
您正在做的事情令人担忧。 Rails 中的“关注”一词实际上只是模糊地表示“混合到类中的模块”之类的东西。唯一真正的定义是app/models/concerns 和app/controllers/concerns 是自动加载根,而ActiveSupport::Concern 的存在只是简化了编写mixin 时所需的样板代码。比如你可以使用它的class_methods 宏来缩短上面的代码。
对于关注点应该包含什么或其在应用程序中的作用没有实际定义,也没有任何要求将其混合到多个类中。
但是……
如果您将逻辑从模型(或任何类)中移出并移入模块中,那么如果您随后将其重新包含到模型中,则实际上并没有任何效果。您只是通过将代码改组为多个文件来掩盖代码。
责任的数量和复杂性保持不变。
如果你真的想重新分配职责,你想创建一个可以独立运行并执行工作单元的对象:
class SomeService
def initialize(thing)
@thing = thing
end
def perform
# do something awesome with @thing
end
def self.perform(thing)
new(thing).perform
end
end
这通常被称为 服务对象模式 - 服务模块不是 AFAIK 的东西。这具有一组定义的职责并卸载模型。 ActiveJob 就是这种模式的一个例子。
您正在做的事情被称为方法提取 - 基本上只是将类似上帝的对象拆分为模块,因为modules are good and big classes are evil。对?没有。依旧是神级。这在 Rails 引入关注文件夹的时候变得非常流行。
另一个不容忽视的解决方案是查看模型是否确实做了很多事情,是否应该将其拆分为具有更明确职责的多个模型。