所以我创建了一个类来强制执行发送给它的类实例的每个方法(消息)。
即代码:
|a|
a := Animal new.
a makeSound: 'bark'
应该导致调用“doesNotUnderstand”(即使它存在于类中)并且它应该检查其他帖子和前置条件,我将解释: 如果方法如下所示:
makeSound: aNoise
"%self assert: [canMakeNoise = true]%"
"@self assert: [numOfLegs >= 0]@"
numOfLegs := -1
这意味着从主要方法的帮助,还有一个方法叫做:PREmakeSound,它的实现是:
self assert: [canMakeNoise = true]
以及名为POSTmakeSiund的onther方法,其实现如下:
self assert: [numOfLegs >= 0]
---我的问题是 - 因为每个方法调用都调用了doNotUnderstand,每当我想要实际激活方法时(在我需要的任何东西之后)我怎么能按原样激活它? 希望我的问题很明确......
答案 0 :(得分:2)
您能解释一下您使用doesNotUnderstand
的原因吗?我的第一个想法是在编译期间注入额外的字节代码......
虽然,从doesNotUnderstand
发送消息的方式如下:
self perform: newSelector withArguments: aMessage arguments.
问题在于,如果这条消息很容易以这种方式进入无限循环。
答案 1 :(得分:2)
使用方法包装器可能比使用#doesNotUnderstand更好吗?
使用compiledMethod实例变量创建一个类PrePostMethod。然后,您可以在类的方法字典中安装PrePostMethod的实例,而不是CompiledMethod的实例。
当VM查找消息并获取此PrePostMethod实例而不是CompiledMethod时,它不知道如何处理它。因此,它将向该PrePostMethod对象发送“run:aSelector with:arguments in:receiver”。您可以在此处执行自定义操作,例如检查事件前的状况。
例如:
PrePostMethod>>run: aSelector with: arguments in: receiver
| result |
self checkPrecondition: receiver
result := compiledMethod run: aSelector with: arguments in: receiver
self checkPostCondition: receiver.
^ result
正如Sean所说,另一种解决方案是改变编译这些方法的方式。 您可以在编译之前转换方法的AST,或者更改编译过程本身。例如,使用AST转换方法,您可以转换:
makeSound: aNoise
"%self assert: [ self canMakeNoise]%"
"@self assert: [ self numOfLegs >= 0]@"
numOfLegs := -1
成:
makeSound: aNoise
self assert: [
"The preconditions goes here"
self canMakeNoise ]
^ [ "Original method body + self at the end if there is no return"
numOfLegs := -1.
self ] ensure: [
"The postcondition goes here"
self assert: [ self numOfLegs >= 0 ] ]
一方面,这些解决方案实施起来会更加繁琐,另一方面,它们的性能更高。
HTH