Как я могу добавить методы к классу во время выполнения в Smalltalk? - PullRequest
7 голосов
/ 16 декабря 2010

Я создаю Smalltalk API для веб-службы на основе XML. Служба XML настолько регулярна, что вместо написания методов вручную я решил переопределить #doesNotUnderstand: для динамического добавления методов через MyApi class>>compile:, затем вызвать все методы один раз в рабочей области, затем удалить DNU У меня есть хороший API.

Это прекрасно работает, но передача гигантской нити на #compile: просто кажется мне неправильной; в Python и других языках я смог бы присоединить к классу лямбду с проверкой синтаксиса, чтобы добиться аналогичного эффекта более безопасным способом. E.g.:

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)

против

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

Конечно, должно быть что-то такое же чистое, как версия Python?

Ответы [ 4 ]

4 голосов
/ 18 декабря 2010

Предположим, у вас есть метод шаблона:

SomeClass>>himaker: aName
  Transcript show: 'Hi ...'

Затем вы можете скопировать его в другой класс, просто не забудьте установить селектор и класс если вы не хотите путать системный браузер. Или, если вам все равно, просто установите копию в словаре методов.

| method |

method := (SomeClass>>#himaker:) copy.

method methodClass: OtherClass.
method selector: #boo: .
OtherClass methodDict at: #boo: put: method.

method := method copy.
method selector: #bar: .
method methodClass: OtherClass2.
OtherClass2 methodDict at: #bar: put: method.
4 голосов
/ 17 декабря 2010

Если вы просто хотите, чтобы исходная строка более четко отражала метод:

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

Если вы хотите, чтобы источник проверялся на синтаксис, вы можете начать с использования метода шаблона:

sayHelloTemplate: count
    count timesRepeat: [ Transcript show: 'Hi, NAME' ].

И затем заполните шаблон соответственно, как:

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.

Конечно, все это будет понятнее, если будут извлечены некоторые методы:)

0 голосов
/ 21 декабря 2010

Я бы использовал блок:

himaker := [:name | [:n | n timesRepeat: [Transcript show: 'Hi , ', name, '!']]]
hibob = himaker value: 'bob'.
hialice = himaker value: 'alice'.
hialice value: 2

Вы все еще можете сделать himaker методом

himaker: name
    ^[:n | n timesRepeat: [Transcript show: 'Hi, ', name, '!']]
0 голосов
/ 19 декабря 2010

Ну, компилируй: принимает строку. Если вы хотите что-то более безопасное, вы можете создать парсетное дерево и использовать его.

...