2

我对 smalltalk 不是很熟悉,但是当我初始化自己的类时,我想做的是覆盖另一个“新”类。这与 Class>>bindingOf: 有什么关系?

编辑:

我想要实现的是:如果 ObjectA 调用new,则 ObjectB 处理请求。
ObjectB 与 ObjectA 无关。
这可以通过仅更改 ObjectB 的实现来完成吗?

编辑:我ObjectB在这个故事中是一个ObjectTracer,我想要它做的是表现得有点像ObjectA. 我是否更改ObjectAnew使用类的方法字典的实现以及如何完成?

编辑:这是我想要实现的:

| obj |
obj := ObjectA new.
obj aMethod.

真正发生的事情是,当new被发送到时ObjectA,它被替换为(包装器)提供的实现,就像他们的答案中提到ObjectB的 aka.nice 和 Hernan 一样,处理用于. 从本质上讲,我是否有可能只需要替换's ?ObjectB#doesNotUnderstandObjectA
ObjectBObjectA#new

4

5 回答 5

5

大多数跟踪器、分析器和采样器都监视系统的执行。如果您想开发一个实际修改系统的示踪剂,您必须非常了解它的动态以避免附带影响。

这是示踪剂的基本公式:

  • 您将使用 Tracer (anObjectB) 包装 anObjectA
  • 使用“object”实例变量创建一个 Tracer 类(大多数 Tracers 子类都来自 nil 左右)
  • 在 Tracer 的类端实现#initialize 删除超类指针(超类:= nil)
  • 在 Tracer 的类端实现 #on:anObjectA 以将 anObjectA 存储在 Tracer 的对象中 iv

在 Tracer 的实例端实现#doesNotUnderstand: aMessage,就像这个模板:

doesNotUnderstand: aMessage
"trace the selector then pass the message on"

| result |
aMessage arguments size = 0
  ifTrue:
    [result := object perform: aMessage selector]
  ifFalse:
    [result := object perform: aMessage selector withArguments: aMessage arguments].
^result

在 #perform: send 之前的情况下,您可能会访问 ObjectA 方法字典并将“aMessage”CompiledMethod 替换为另一个。要访问某个类的方法字典,只需在一些 Smalltalks 中发送#methodDictionary 或#>>。

你可以在你知道如何进行反射的情况下放置一个新的编译方法,甚至替换整个字典:

| methodDictionary |
methodDictionary := MethodDictionary new.
methodDictionary at: #basicNew put: (Object compilerClass new
                                compile: 'basicNew ^Point basicNew'
                                in: Object
                                notifying: nil
                                ifFail: []) generate.

请注意,您不需要将方法驻留在要评估的 MethodDictionary 中。查看#valueWithReceiver:arguments 的发件人:

最后你发送到你的新对象

anObjectA := MyTracer on: ObjectA new.
anObjectA example1.

是的,你也可以把它放在 ObjectA 的新方法中。你最好阅读一些关于 Smalltalk 中反射的章节,网络上有很多内容,因为 Smalltalk 是进行计算反射的最佳平台。

于 2012-12-09T20:21:53.857 回答
3

不,#bindingOf: 与编译方法有关。

可通过多种方法访问的变量,如全局变量、类变量或池变量,通过在方法文字中存储相同的绑定来共享。绑定是一种关联,其键是变量名,值是变量值。

当您的代码使用变量时,该方法将#value 发送到引擎盖下的绑定,并且当您将值存储到变量中时,它会发送#value:

但是请注意,根据 Smalltalk 的风格,这些操作可能会在字节码中进行优化,并替换为直接访问绑定的第二个实例变量(值)。

因此编译器需要检索 bindingOf: aSymbol 才能访问任何共享变量,其中 aSymbol 是变量的名称。因为变量访问的范围取决于类(只有一个类及其子类可以访问类变量......),所以会查询该方法编译到的类以获取该信息。

如果你想覆盖 YourClass 中的实例创建,只需在类侧覆盖#new(我们说 YourClass class>>#new)。如果您使用 Squeak/Pharo 方言,大多数情况下,您可以通过在实例端 (YourClass>>#initialize) 重写 #initialize 来实现特定的实例化,因为 #new 将调用 #initialize。

编辑

如果您想使用 ObjectTracer 捕获 #new 到 ObjectA 的发送,您可以执行以下操作:

| theTrueObjectA |
theTrueObjectA := ObjectA.
[Smalltalk globals at: #ObjectA put: (ObjectTracer on: ObjectA).
"insert the code you want to test here"
ObjectA new]
    ensure: [Smalltalk globals at: #ObjectA put: theTrueObjectA].

EDIT2最后一句可以替换为ensure: [ObjectA xxxUnTrace]

然而,现代的 squeak 调试器是侵入性的,它本身会向 ObjectTracer 发送许多消息,导致其他调试器弹出......您应该首先打开一个 Preferences 窗口并禁用 logDebuggerStackToFile。

请注意,所涉及的机制是消息#doesNotUnderstand:由对象在它不理解消息时发送。ObjectTracer 覆盖 #doesNotUnderstand: 以弹出调试器。

你可以继承 ProtoObject 来安装你自己的 #doesNotUnderstand: 处理(就像只是在转录或文件中写一些东西)。

另请注意,ObjectTracer #inheritsFrom: ProtoObject 和 ProtoObject 本身 #respondsTo: 许多消息不会被 ProtoObject>>#doesNotUnderstand 捕获:

最后说明:我在上面用#表示可以理解的消息。

编辑 3:一种可能的解决方案是定义一种新的 ObjectTracer,其中包含两个实例变量 tracedObject 和 messageMapping 以及此实例创建:

MessageInterceptor class>>on: anObject interceptMessages: aDictionary
    "Create an interceptor intercepting some messages sent to anObject.
    aDictionary keys define message selectors that should be intercepted.
    aDictionary values define block of code that should be evaluated in place.
    These blocks always take one argument for passing the traced Object,
    plus one argument per message parameter.
    snip..."

MessageInterceptor>>doesNotUnderstand: aMessage
    mapping := messageMapping at: aMessage selector
        ifAbsent:
            ["We don't intercept this message, let the tracedObject handle it"
            ^aMessage sendTo: tracedObject].
    ^mapping valueWithArguments: {tracedObject} , aMessage arguments

例如,您可以这样使用它:

| interceptor |
interceptor := MessageInterceptor on: ObjectA interceptMessages:
    ({#new -> [:class | ObjectTracer on: class new]} as: Dictionary).
[Smalltalk globals at: #ObjectA put: interceptor.
"insert the code you want to test here"
ObjectA new yourself]
    ensure: [interceptor xxxUnTrace].
于 2012-12-09T16:06:47.737 回答
1

对象可以理解的方法存储在该对象的类中。如果你想重写实例创建 #new 方法发送到你在类的类上操作过的类,即它的元类

meta := ObjectA class.

您可以在此处阅读有关元类的信息:CSE 341:Smalltalk 类和元类

每个类,包括类的类(元类),都将它们的实例可以理解的消息存储在方法字典中,在 Pharo 中是名为 methodDict 的实例变量。如果您希望一个对象能够接收一条消息,您必须在该字典中插入一个 CompiledMethod,它将作为您的消息发送的结果执行。Smalltalk 中的类有一个方便的编译方法,可用于在类中安装 CompiledMethods,例如

ObjectA 
    compile: 'new
        ^ObjectB new'
    classified: 'instance creation'

如果您的 ObjectA 已经定义了 #new 方法,这将覆盖它。您必须缓存旧的 CompiledMethod 以保留它以供以后恢复。

在现实生活中,您将使用 Method Wrappers 来实现您想要在这里实现的目标,请查看Wrappers to the Rescue

于 2017-02-12T20:04:05.040 回答
0

在我看来,您只需要实现 ObjectA 类>>new。你可以这样做:

new
  inst := self basicNew.
  ^ ObjectB wrapping: inst
于 2012-12-10T00:07:10.897 回答
0

在我碰巧打开的当前 Moose (Pharo) 图像中,有 171 个新的实现和 1207 个初始化的实现。这表明您更有可能需要覆盖初始化而不是新的。浏览它们时,我发现以下想要覆盖 new 的常见情况:

  • 抽象类,只创建我的子类
  • 单例,改为调用 uniqueInstance
  • 使用默认值创建
  • 价值对象
  • 与其他方言的兼容性
于 2012-12-09T16:53:01.603 回答