Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Deleted note for Groovy 1.1

...

Suppose we have the following classes (inspired by this):

Code Block

class SquarePeg {
    def width
}

class RoundPeg {
    def radius
}

class RoundHole {
    def radius
    def pegFits(peg) {
        peg.radius <= radius
    }
    String toString() { "RoundHole with radius $radius" }
}

...

To get around this problem, we can create an adapter to make it appear to have the correct interface. It would look like this:

Code Block

class SquarePegAdapter {
    def peg
    def getRadius() {
        Math.sqrt(((peg.width/2) ** 2)*2)
    }
    String toString() {
        "SquarePegAdapter with peg width $peg.width (and notional radius $radius)"
    }
}

We can use the adapter like this:

Code Block

def hole = new RoundHole(radius:4.0)
(4..7).each { w ->
    def peg = new SquarePegAdapter(peg:new SquarePeg(width:w))
    if (hole.pegFits(peg))
        println "peg $peg fits in hole $hole"
    else
        println "peg $peg does not fit in hole $hole"
}

Which results in the following output:

Code Block

peg SquarePegAdapter with peg width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0
peg SquarePegAdapter with peg width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0
peg SquarePegAdapter with peg width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0
peg SquarePegAdapter with peg width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0

...

Let's consider the same example again using inheritance. First, here are the original classes (unchanged):

Code Block

class SquarePeg {
    def width
}

class RoundPeg {
    def radius
}

class RoundHole {
    def radius
    def pegFits(peg) {
        peg.radius <= radius
    }
    String toString() { "RoundHole with radius $radius" }
}

An adapter using inheritance:

Code Block

class SquarePegAdapter extends SquarePeg {
    def getRadius() {
        Math.sqrt(((width/2) ** 2)*2)
    }
    String toString() {
        "SquarePegAdapter with width $width (and notional radius $radius)"
    }
}

Using the adapter:

Code Block

def hole = new RoundHole(radius:4.0)
(4..7).each { w ->
    def peg = new SquarePegAdapter(width:w)
    if (hole.pegFits(peg))
        println "peg $peg fits in hole $hole"
    else
        println "peg $peg does not fit in hole $hole"
}

The output:

Code Block

peg SquarePegAdapter with width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0
peg SquarePegAdapter with width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0
peg SquarePegAdapter with width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0
peg SquarePegAdapter with width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0

...

As a variation of the previous examples, we could instead define the following interface:

Code Block

interface RoundThing {
    def getRadius()
}

We can then define an adapter as a closure as follows:

Code Block

def adapter = {
    p -> [getRadius:{Math.sqrt(((p.width/2) ** 2)*2)}] as RoundThing
}

And use it like this:

Code Block

def peg = new SquarePeg(width:w)
if (hole.pegFits(adapter(peg)))
// ... as before

...

Here is how the example would work using that feature:

Code Block

def peg = new SquarePeg(width:w)
peg.metaClass.radius = Math.sqrt(((peg.width/2) ** 2)*2)

After you create a peg object, you can simply add a property to it on the fly. No need to change the original class and no need for an adapter class.

Note that at the moment you have to be using Groovy 1.1 (currently in beta) and you have to initialise the new MetaClass with the following code:

Code Block

GroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle()

The need for this last line may go away before the final release of Groovy 1.1.