我对 ruby 中模块的使用感到困惑。具体来说,当模块混入类中时,模块中的对象实例变量/方法应该如何访问。考虑以下示例。
class A
include B
def initialize(my_var)
@my_var = my_var
@result = nil
end
def get_result
@result = foo
end
end
module B
def foo
"Result of #{@my_var}"
end
end
在上面的例子中,模块B的设计方式是,它期望封装对象定义变量my_var。这是一个好的设计吗?感觉不对,因为在这种情况下,模块 B 在某种程度上依赖于类 A。构造模块 B 的另一种方法是这样的。
module B
def foo(my_var)
"Result of #{my_var}"
end
end
在此版本中,该方法接受一个参数并执行必要的计算并返回一个值。这更清晰,但这可能是一个人为的例子,因为在许多现实世界的场景中,事情要复杂得多。
而且,它不仅仅是关于实例变量。我认为同样的问题也适用于实例方法。我们是否可以构造一个模块,期望封装对象具有某些方法和/或实例变量?在这种情况下设计解决方案的正确方法是什么?
我查看了与此问题密切相关的几个 stackoverflow 线程,但找不到满意的答案。预先感谢您的所有回答
最佳答案
在第二个示例中,foo
并不是真正的方法,而是一个过程。方法的区别在于它具有对接收者的特权访问权限,即 self
。但是你的第二个foo
没有任何用处!那么,为什么要让它成为一种方法呢?您可以使用 BASIC 中的过程执行相同的操作。
访问 self
完全没问题在方法中,这就是方法的用途。
Ruby 本身无法声明 mixin 到底期望什么 self
不过,除了文档之外,还是要完成它的工作。但这很常见:Comparable
预计self
回复<=>
, Enumerable
预计self
回复each
,例如。
我称这些为“杠杆混合”,因为它们的作用就像一个杠杆:你只需要自己提供一个很小的力(一个 each
方法),但它们允许你做很多事情 繁重的工作。
这与 Ruby 中的其他类型的期望没有太大不同。 Array#join(sep)
预计sep
回复to_str
以及要响应的数组元素 to_s
, Array#[](idx)
预计idx
回复to_int
。 Range
成员资格测试方法期望左侧元素响应 <=>
,Range
迭代方法( each
、 to_a
、 step
、...)期望左侧元素响应 succ
。 Enumerable#sort
期望可枚举的元素响应 <=>
等等等等。
将期望寄托在self
上与将它们放置在任何其他物体上没有太大区别。
这些“期望”通常称为“协议(protocol)”。对象遵守协议(protocol)(它们支持的消息集以及它们如何对这些消息使用react),并且依赖于其他对象的协议(protocol)。例如,您可以说 Enumerable
mixin 取决于 Iteration
协议(protocol)。然而,除了简单的文档之外,Ruby 语言中没有任何结构来表达此类协议(protocol)。
但是,一般来说在 Ruby 中使用实例变量并不是很面向对象。 OO 是关于消息传递的,但 Ruby 只为方法而不是变量提供消息传递(例如,与 Self 不同,实例变量是通过消息查找的),因此您应该更喜欢方法而不是变量。如果您担心 getter 泄漏信息或 setter 破坏某些不变量,请将它们设为 private
.
关于Ruby Mixin 对对象的依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27297392/