Grails:如何使用非域抽象基类模拟域对象的属性

时间:2015-12-16 13:23:44

标签: grails spock

我有一个带有域类Monster的grails 2.2.4应用程序:

class Monster {
    int aggression
}

我可以像这样模拟和测试它:

import spock.lang.*
class MonsterSpec extends Specification {
    def "property mocks work"() {
        given:
        def m = Mock(Monster)
        m.aggression >> 5

        expect:
        m.aggression == 10
    }
}

最近我决定给它一个抽象基类(一个域对象本身),这样我就可以在我的许多Monster类中共享方法实现:

abstract class Entity {} // Not under /domain

class RefactoredMonster extends Entity {
    int aggression
}

然后,一千个简单的测试都破了,就像这样:

import spock.lang.*
class MonsterSpec extends Specification {
    def "property mocks work"() {
        given:
        def m = Mock(RefactoredMonster)
        m.aggression >> 10

        expect:
        m.getAggression() == 10 // This works

        and:
        m.aggression == 10 // This fails!  m.aggression is null!
    }
}

地球上发生了什么?如果我使Entity具体化,问题就会消失,但当然我不能保留任何Monster个对象,因为Hibernate不知道如何处理Entity(而且我不想让Entity成为域名对象,但我想如果我真的必须这样做,我会这样做。

我错过了什么?

1 个答案:

答案 0 :(得分:1)

问题是GORM希望超类成为域类。

使用Grails 2.2.4所具有的Groovy 2.0,您可以使用编译时mixins向类添加方法。这允许在没有继承的情况下重用方法。

Entity可以保留为域类,但必须是具体类。然后,不要使用子类,而是将其用作mixin。

@Mixin(Entity)
class RefactoredMonster {
    int aggression
}

另一种选择

由于你需要能够覆盖方法,正如你所说,Mixins已经出局了。

从更高层面来看,潜在的问题是架构/设计。继承意味着代表 is-a 关系(例如,狗是动物)。但是当继承主要用作重用方法的方法时,它可能导致......一团糟。

放弃继承并选择 has -a (委托)可能会更好。这将允许您重用行为并在需要时覆盖它。不幸的是,Groovy 2.0并不支持@Delegate。因此,以下示例将具有比Groovy 2.4中编码的相同内容更多的样板代码:

interface Flier {
    def fly();
}

class FlierImp {
    def fly() { "I'm fying! WOOT!" }
}

class RealDuck implements Flier {
    def flier

    RealDuck() {
        flier = new FlierImp() // Purposely not using injection
    }

    def fly() {
        flier.fly()
    }
}

class RubberDuck implements Flier {
    def fly() { "I don't fly" }
}

def duck = new RealDuck()
def rubberDuck = new RubberDuck()

assert duck.fly() == "I'm fying! WOOT!"
assert rubberDuck.fly() == "I don't fly"

在上面的示例中,RealDuckRubberDuck代表了域类(这就是我没有注入飞行器的原因)。通过接口要求飞行行为,并通过仅实现行为(FlierImp)的类或直接实现它来实现,如RubberDuck所示。< / p>