Ruby的版本是:
% ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]
我发现如果我们在“钻石”中混合使用该怎么办?在Ruby中形成。
以下是一个例子:
module M3; end
module M1
prepend M3
end
module M2
prepend M3
end
class Base
include M1
include M2
end
p Base.ancestors # [Base, M3, M2, M1, Object, Kernel, BasicObject]
结果为[Base, M3, M2, M1, Object, Kernel, BasicObject]
。
即使您在M2
类中将模块include
的mixin类型从prepend
更改为Base
,结果也是相同的:
module M3; end
module M1
prepend M3
end
module M2
prepend M3
end
class Base
include M1
prepend M2 # <= change mixin type
end
p Base.ancestors # [Base, M3, M2, M1, Object, Kernel, BasicObject]
结果也是[Base, M3, M2, M1, Object, Kernel, BasicObject]
。这对我来说很奇怪。
Ruby如何在&#39; diamond&#39;中解决方法查找问题? MIXIN?
注意)我已经在Ruby中了解了方法查找的基础知识,https://docs.ruby-lang.org/en/trunk/syntax/refinements_rdoc.html#label-Method+Lookup。
答案 0 :(得分:3)
我的理解是:
include
和prepend
在每个步骤的相关模块/类之间建立了部分关系(祖先关系),让我们从:
开始module M1; end
module M2; end
module M3; end
class Base; end
并按照每个步骤操作。前三个步骤应该是微不足道的:
Base.ancestors
#=> [Base, Object, Kernel, BasicObject]
M1.prepend M3
M1.ancestors
# => [M3, M1]
M2.prepend M3
M2.ancestors
#=> [M3, M2]
现在,您关键的第一步是Base.include M1
。这会将M1
(整个[M3, M1]
块)的祖先插入Base
之前Object
的右侧:
Base.include M1
Base.ancestors
#=> [Base, M3, M1, Object, Kernel, BasicObject]
下一步是Base.prepend M2
。这会尝试将M2
(整个[M3, M2]
块)的祖先插入Base
的左侧。但请注意,这会导致Base
和M3
之间存在矛盾关系。
Base.prepend M2
Base.ancestors
#=> Cannot be [M3, M2, Base, M3, M1, Object, Kernel, BasicObject]
由于已经确定M3
出现在Base
的右侧,因此放置[M3, M2]
的最佳方法是将其置于Base
的右侧1}}:
Base.prepend M2
Base.ancestors
#=> [Base, M3, M2, M1, Object, Kernel, BasicObject]
将M2
放在Base
的右侧可能会与Base.prepend M2
的意图相矛盾。但是可以取消/修改以适应现场,而Base
和M3
之间已建立的关系不能在以后取消。
事实上,当无法满足已建立的关系时,就会出现错误:
module M4; end
module M5; end
M4.include M5
M5.include M4 #>> ArgumentError: cyclic include detected
答案 1 :(得分:2)
这里有一些可能有用的东西。一种是回忆include
和prepend
的默认行为,即如果尚未将模块添加到此模块或其中一个祖先中,则只添加该模块。
接下来,我要看一下两个独立的步骤,而不是一次性执行include M1
和prepend M2
。
如果问题是模块定义,那么我们只需要:
class Base
include M1
end
Base.ancestors
现在是[Base, M3, M1, Object, PP::ObjectMixin, Kernel, BasicObject]
,这可能是您所期望的。
接下来,如果我们这样做
class Base
prepend M2
end
Base.ancestors
现在是[Base, M3, M2, M1, Object, PP::ObjectMixin, Kernel, BasicObject]
原因是M2
预先M3
但M3
已经在Base
的祖先中M3
的位置保持不变。但是,由于M2
取决于M3
,这意味着M2
必须在 M3
之后。结果是M2
显示在M3
之后,而不是第一个条目,即使它位于Base
之前。
答案 2 :(得分:1)
虽然这里的两个现有答案都非常符合逻辑并且解释了一切都很好,但我还是会删除对文档的引用:
[...] 如果此模块尚未添加到mod或其祖先之一。 [...]
-Module#prepend_features
重点是我的。也就是说,include M
,以及本地类祖先的extend M
(和var myObj = {firstname: "Stevey",
printFirstName() {
console.log(this.firstname);
}}
function printNames({printFirstName}) {
printFirstName();
}
printNames(myObj);
)都是NOOP,如果已经在祖先链中找到了模块。