我试图通过将方法组织到单独的模块中来模块化一些Ruby代码。最初我有这样的事情:
class Joe
attr_accessor :name
def self.arms
2
end
def self.legs
2
end
end
我尝试过这样的事情:
class Joe
extend Person
end
module Person
include Name
include Arms
include Legs
end
module Name
attr_accessor :name
end
module Arms
def arms
2
end
end
module Legs
def legs
2
end
end
但是,无效的部分是attr_accessor
。我尝试了include
/ extend
,def self.included(base); base.extend
的所有不同组合,但我似乎无法找到合适的组合来使所有内容协同工作。我怎么能这样做?
更新:我认为我遗漏的部分是每个模块都可能有实例方法和类方法。所以我现在有这样的事情:
class Joe
include Person
end
module Person
include Name::InstanceMethods
include Arms::InstanceMethods
include Legs::InstanceMethods
def self.included(base)
base.extend Name::ClassMethods
base.extend Arms::ClassMethods
base.extend Legs::ClassMethods
end
end
module Name
module ClassMethods; end
module InstanceMethods
attr_accessor :name
end
end
module Arms
module ClassMethods
def arms
2
end
end
module InstanceMethods; end
end
module Legs
module ClassMethods
def legs
2
end
end
module InstanceMethods; end
end
虽然这有效,但感觉很乱。感觉就像Person
模块对实例方法和类方法了解得太多。如果我要修改Name
模块以删除空/未使用的ClassMethods
模块,我还必须修改Person
类。
答案 0 :(得分:10)
include
在Module
中定义,因此只能在模块和类(模块)上调用。它通过调用append_features
将给定模块中的常量,(实例)方法和(模块)变量添加到接收器。
extend
在Object
中定义,即它不限于模块和类。它将给定模块中的实例方法添加到接收器,或者更确切地说,添加到接收器的单例类。
这是一个带有实例方法hello
的示例模块:
module Mod
def hello
"Hello from #{self.class} '#{self}'"
end
end
如果我们extend
一个实例(而不是一个类),那么hello
将成为实例方法:
str = 'abc'
str.extend(Mod)
str.hello
#=> "Hello from String 'abc'"
如果我们扩展一个类,那么hello
将成为类方法:
String.extend(Mod)
String.hello
#=> "Hello from Class 'String'"
这是因为类方法实际上只是在类的单例类中定义的实例方法。
也就是说,有几个选项可以通过调用extend
和/或include
来定义类和实例方法:
extend
和include
这是最基本的一个,您可以将include Name
从人员转移到Joe
:
module Person
include Arms, Legs
end
class Joe
extend Person
include Name
end
extend
和include
或者你可以让Person
成为extend
和include
其他模块的类,并将其用作Joe
的超类:
class Person
extend Arms, Legs
include Name
end
class Joe < Person
end
接下来的选项涉及一些Ruby魔术 - 他们使用回调在include
上调用extend
,反之亦然:
include
extended
您可以在include Name
内使用Person
回调至module Person
include Arms, Legs
def self.extended(mod)
mod.include(Name)
end
end
class Joe
extend Person
end
:
extend
included
include Person
或者您可以Joe
内extend
使用extended
回调来致电module Person
include Name
def self.included(mod)
mod.extend Arms, Legs
end
end
class Joe
include Person
end
:
Joe
3和4从Person
内部看起来很干净但是包含或扩展PHANTOMJS_BIN
可能并不明显(甚至可能令人困惑?)也定义了类或实例方法。
答案 1 :(得分:0)
为了正确获得您想要的功能,您必须将extend
更改为include
并在模块中提供included
块。这样你就可以在一个模块中定义实例和类方法,只需include
模块并适当地组织你的方法。
可能需要一些代码重新组织,但为了帮助解释更多,您可以read this article about how modules in Ruby work.
如果仍不清楚,您可能需要查看class_eval
。