在Squeak中执行和签订合同的方法

时间:2014-12-12 08:36:45

标签: oop smalltalk squeak design-by-contract

所以我创建了一个类来强制执行发送给它的类实例的每个方法(消息)。

即代码:

|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,每当我想要实际激活方法时(在我需要的任何东西之后)我怎么能按原样激活它? 希望我的问题很明确......

2 个答案:

答案 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