混合状态的 Mixins

3

我正在阅读《Pickaxe Book》,作者提供了以下示例作为一种在不使用实例变量的情况下给模块/混入状态的技巧:

...该模块可以使用一个模块级哈希表,通过当前对象ID进行索引,以存储特定于实例的数据...

module Test
  State = {}
  def state=(value)
    State[object_id] = value
  end
  def state
    State[object_id]
  end
end

class Client
  include Test
end

c1 = Client.new
c2 = Client.new
c1.state = 'cat'
c2.state = 'dog'
c1.state # => "cat"
c2.state # => "dog"

我不太清楚这个是如何工作的。特别是object_idobject_id方法是如何以这种方式访问Client实例的?我尝试使用length看是否可以根据它进行索引,但是我得到了以下结果:

NameError: undefined local variable or method `length' for #<Client:0x00000000ecc570>

我希望能够确保自己理解这里所发生的原理。


1
在 Stack Overflow 上不要使用 <pre> 标签来展示代码,而是使用缩进 4 个空格的方式。因为 <pre> 标签不能正确地进行 HTML 转义,所以类似标签的内容(例如 <Client:0x00000000ecc570>)将无法显示。 - Amadan
哦,糟糕!谢谢你的提示。 - Nathaniel Miller
2个回答

4
对象的 object_id 方法如何以这种方式访问 Client 实例?
  1. The state=() method is inherited from the Test module when it is included. Included modules create an anonymous class that is inserted right above the including class in the inheritance chain.

  2. This line:

    c1.state = 'cat'
    

    is equivalent to:

    c1.state=('cat')
    

    And when c1 calls state=(), inside the state=() method self will be equal to c1. Inside a def, self is equal to the object that called the method.

  3. When you call a method with no receiver, then self is the implicit receiver. Inside state=():

    def state=(value)
      State[object_id] = value
    end
    

    the object_id() method is called with no receiver, so self becomes the receiver. As a result, the line:

    State[object_id] = value
    

    is equivalent to:

    State[self.object_id] = value
    

    which is equivalent to:

    State[c1.object_id] = value
    

谢谢!第三点是我一直没有理解的。现在感觉非常清晰了。 - Nathaniel Miller

1
如果Client包含Test,且c1是一个Client,那么object_id就是从Object继承而来的c1.object_id。每个Ruby对象都保证有唯一的object_id。并非所有对象都保证具有length(此外,许多对象将具有非唯一的length,例如"f"[8]共享length 1)。

抱歉。我理解object_id的作用以及继承的原理,但是当object_id在这里被使用时,它与使用self.object_id是相同的。这样说对吗?我一直认为通过[]State上调用了object_id。哈哈哈,我真傻。这就是为什么我尝试使用length方法的原因...因为State.methods显示了length作为一个选项。我知道它不会成为一个好的索引,但我不知道为什么它根本不起作用。现在看来非常明显了。 - Nathaniel Miller
1
是的,在类内部,您可以调用每个方法而无需显式接收器(实际上对于私有方法,这是唯一的调用方式,除非使用 send 欺骗)。因此,object_idself.object_id 相同;并且考虑到 Test 包含在 Client 中,因此 selfc1c2 - Amadan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接