如何在Smalltalk中在运行时向类添加方法?

Ben*_*ack 7 smalltalk squeak

我正在为基于XML的Web服务构建Smalltalk API.XML服务非常规则,而不是手工编写方法,我想我只是覆盖#doesNotUnderstand:动态添加方法MyApi class>>compile:,然后在工作区中调用所有方法一次,然后删除DNU并拥有我的漂亮API.

这很好用,但通过一根巨大的绳子#compile:让我感到非常不对劲; 在Python和其他语言中,我能够将一个很好的语法检查的lambda附加到一个类,以更安全的方式实现类似的效果.例如:

def himaker(name):
    def hello(self, times):
        for x in xrange(times):
            print "Hi, %s!" % name
    return hello
class C(object): pass
C.bob = himaker('Bob')
C.jerry = himaker('Jerry')
a = C()
a.bob(5)
Run Code Online (Sandbox Code Playgroud)

SomeObject>>addHello: name
    | source methodName |
    methodName := 'sayHello', name, 'Times:'.
    source := String streamContents: [ :s |
         s nextPutAll: methodName, ' count'.
         s nextPut: Character cr.
         s nextPut: Character tab.
         s nextPutAll: 'count timesRepeat: [ Transcript show: ''Hi, ', name, '!'' ].' ]
    SomeObject class compile: source
Run Code Online (Sandbox Code Playgroud)

当然必须有像Python版本一样干净的东西?

Sea*_*ris 4

如果您只是想让源字符串更清楚地反映该方法:

SomeObject>>addHello: name
  | methodTemplate methodSource |
  methodTemplate := 'sayHello{1}Times: count
  count timesRepeat: [ Transcript show: ''Hi, {1}!'' ].'.   
  methodSource := methodTemplate format: { name }.
  self class compile: methodSource.
Run Code Online (Sandbox Code Playgroud)

如果您希望对源代码进行语法检查,您可以从这样的模板方法开始:

sayHelloTemplate: count
    count timesRepeat: [ Transcript show: 'Hi, NAME' ].
Run Code Online (Sandbox Code Playgroud)

然后相应地填写模板,例如:

addHello2: name
    | methodTemplate methodSource |
    methodTemplate := (self class compiledMethodAt: #sayHelloTemplate:) decompileWithTemps.
    methodTemplate selector: ('sayHello', name, 'Times:') asSymbol.
    methodSource := methodTemplate sourceText copyReplaceAll: 'NAME' with: name.
    self class compile: methodSource.
Run Code Online (Sandbox Code Playgroud)

当然,如果提取一些方法,所有这些都会更清楚:)