Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Take the following classes.

Code Block
borderStylesolid
titleWombat.groovy
borderStylesolid
class Wombat {

    int getStrength() { 5 } // small, weak
    int getIntelligence() { 2 } // not too swift this way either
    int getDexterity() { 6 } // not to swift
    int getConstitution() { 14 } // wombats are healthy
    int getWisdom() { 2 } // poor decision making
    int getCharisma() { 15 } // wombats are cute
    int getHitDice() { 2 }

    /**
     * wombats turn very quickly for their size and agility
     */
    getTurningSpeed() {
        5
    }
}
Code Block
borderStylesolid
titleMobile.groovy
borderStylesolid
class Mobile {
    heading = 0
    locationX = 0
    locationY = 0

    walk(seconds) {
        distance = getWalkingSpeed() * seconds
        locationX = distance * applyTrigIDontRememberTo(heading)
        locationY = distance * applyMoreTrigIDontRememberTo(heading)
        distance
    }

    turnRight(seconds) {
        heading += seconds * getTurningSpeed()
    }

    turnLeft(seconds) {
        heading -= seconds * getTurningSpeed()
    }

    double getTurningSpeed() {
        ((getDexterity() + getIntelligence()) / 2) / 3
    }

    getWalkingSpeed() {
        (((getStrength() + getDexterity()) / 2) / 3) * 10
    }

    abstract getDexterity();
    abstract getStrength();
    abstract getIntelligence();
}
Code Block
borderStylesolid
titleFighterType.groovy
borderStylesolid
class FighterType implements CharacterClass {

    getThaco() {
        20 - getHitDice() - (Bonuses.getStrengthModifier(getStrength()))
    }

    abstract getHitDice();
    abstract getStrength();
}

interface CharacterClass {
    int getThaco();
}

...

At compile time mixin information can be provided, in which case all instances of the class receiving the mixin have the mixed traits

Code Block
borderStylesolid
titleWombat.groovy
borderStylesolid
class Wombat {
    mixin(Mobile)
    ...
}

This says that all instances of Wombat are Mobile. An alternate for this is to put it on the class declaration line:

Code Block
borderStylesolid
titleWombat.groovy
borderStylesolid
class Wombat with Mobile {
    ...
}

...

Methods the mixed in instance requires from the host instance can be declared abstract. It is worth considering if they can also be left undeclared and then invoked via dynamic dispatch - this is open for discussion. It is useful to document the methods the mixin requires, though, so I used the abstract declaration here.

Dynamic Mixing

Code Block
borderStylesolid
titleSample.groovy
borderStylesolid
george = new Wombat()
george.mixin(FighterType)

or

Code Block
borderStylesolid
titleSample.groovy
borderStylesolid
george = new Wombat().mixin(FighterType)

adds the FighterType mixin to the Wombat instance referenced by George. FighterType is only available on that particular instance, so:

Code Block
borderStylesolid
titleWombat.groovy
borderStylesolid
george = new Wombat().mixin(FighterType)
william = new Wombat()

georage.getThaco() // returns 19 (20 - 2 - (-1))
william.getThaco() // error

...

Adding a single method at runtime has a bit of syntactic sugar, as follows:

Code Block
borderStylesolid
titleSample.groovy
borderStylesolid
george = new Wombat()
george.mixin("say") { |something| println something }
george.say("Hello, world!")

// notice closure executes in context of george, with the mixed in say(..) method
george.mixin("sayHello") { say("hello") }
george.sayHello()

...

The mixin class is allowed to use a constructor which takes args, so:

Code Block
borderStylesolid
titleMyMixin.groovy
borderStylesolid
class MyMixin {
    MyMixin(thing) { ... }
}

anArg = "wibble!"
george.mixin(MyThing, anArg)

...