我读了这篇文章:Ruby 模块 - 包含做结束块 - 但仍然不知道何时会在模块中使用self.included do ... end
块。
这篇文章说,当你包含模块时,块中的代码将运行,但如果模块的唯一目的是被包含,那有什么意义呢?无论如何,该代码不需要运行吗?该块不需要在那里才能运行该代码,对吗?
以下两者之间有什么区别:
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
...
end
end
与。
module M
def self.some_class_method
...
end
scope :disabled, -> { where(disabled: true) }
end
这两个例子有什么区别?
第一个代码块将ClassMethods
中的类方法添加到包含类中,并对其调用scope
方法。第二个不执行这些操作,并且将导致NoMethodError
,因为模块没有scope
类方法。 包含模块后,self.some_class_method
将不再在包含类上可用。
有关模块包含如何在 Ruby 中工作的完整故事,请阅读我的答案:
从 Ruby 中的模块继承类方法/混合
如果模块的唯一目的是包含模块,那么self.included
的意义何在?
包含不是模块的唯一目的。它们还用于其他事情,例如命名空间或简单地存储各种类方法,然后可以在模块本身上调用这些方法。
为什么 Ruby 不自动包含类方法?
从理论上讲,Ruby可以自动将模块中定义的所有类方法添加到包含类中,但实际上这将是一个坏主意,因为您将无法再选择是否要包含类方法 — 所有类方法每次都会被包含,无论它们是否打算被包含。请考虑以下示例:
module M
def self.class_method
"foo"
end
def self.configure_module
# add configuration for this module
end
end
class C
include M
end
在这里,configure_module
方法显然不应该添加到C
,因为它的目的是设置模块对象的配置。但是,如果我们对类方法具有自动包含功能,您将无法阻止它被包含。
但是所有实例方法都已包含在内!那怎么行呢?
模块中的实例方法只有在包含在类中时才真正有用,因为模块不能有实例,只有类可以。因此,在模块中,每个实例方法都应包含在某个地方才能工作。
模块上的"class"方法是不同的,因为它可以在模块本身上调用,因此无论它是否也添加到包含类中,都可以很好地使用它。这就是为什么您最好在那里有选择的原因。
module M
# self.included is the hook which automatically runs when this module is included
def self.included(base)
puts 'this will be printed when this module will be included inside a class'
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
def print_object_class
self.class.name # here self will be the object of class which includes this module
end
module ClassMethods
def print_class_name
self.name # Here self would be the class which is including this module
end
end
end
我尝试修改上述module
以帮助您了解模块(关注点)如何帮助代码可重用性
self.included
是一个钩子,当模块包含在类中时会自动运行。
module ClassMethods
中声明的任何方法都将成为包含此模块的类的类方法
在module ClassMethods
之外声明的任何方法都将成为类(包括此模块)的实例方法
对于 Ex,假设有一个类 Product 并且您已将模块包含在其中
class Product < ActiveRecord::Base
include M
puts 'after including M'
end
如果您在 rails 控制台中尝试此示例,您会注意到,一旦模块M
包含在模块运行的class Product
钩子中,并且
this will be printed when this module will be included inside a class
这是打印在控制台上的
之后after including M
这将打印在控制台上。
您也可以尝试以下命令
Product.disabled # a scope with name 'disabled' is avaialble because of including the module M
Product.print_class_name # Outputs => 'Product' This method is available to class with the help of module M
Product.new.print_object_class #Outputs => 'Product'
它还提供可重用性,将此模块 M 包含在任何类中,并且该类可以访问模块中描述的所有方法。
而你的第二个例子仅仅是基本模块的例子
module N
def self.abc
puts 'basic module'
end
end
现在abc
模块中定义的方法只能使用此模块访问
N.abc # outputs 'basic module'
class Product < ActiveRecord::Base
include N
end
产品.abc #raises 异常,在类产品上找不到方法 Product.new.abc #raises 异常,在类 Product 的对象上找不到方法
我希望这可以帮助您更好地理解模块的概念。 如果您仍然有任何疑问,请告诉我。