这段代码实际上并没有定义类方法:
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是类中的一个模块时,您将该模块添加到该类的祖先链中。因此,它可以调用模块的实例方法,就好像它在它自己的位置一样。您可以将其与extend将模块的方法导入类(test_code成为类方法)进行对比。
因此,在包含时ClassMethods使用内部模块扩展类的模式:
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存在,它只是简化了编写 mixin 时所需的样板代码。例如,您可以使用它的宏来缩短上述代码。app/controllers/concernsActiveSupport::Concernclass_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 就是这种模式的一个例子。
您正在做的事情被称为方法提取 - 基本上只是将像上帝一样的对象拆分为模块,因为模块是好的,大类是邪恶的。正确的?没有。依旧是神级。这在 Rails 引入关注文件夹的时候变得非常流行。
另一个不应忽视的解决方案是查看模型是否确实做了很多工作,并且应该将其拆分为具有更明确定义的职责的多个模型。