# Adapter Pattern

version 2.4.21

The Adapter Pattern (sometimes called the wrapper pattern) allows objects satisfying one interface to be used where another type of interface is expected. There are two typical flavours of the pattern: the delegation flavour and the inheritance flavour.

## 1. Delegation Example

Suppose we have the following classes:

    class SquarePeg {
        def width
    }

    class RoundPeg {
        def radius
    }

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

We can ask the `RoundHole` class if a `RoundPeg` fits in it, but if we ask the same question for a `SquarePeg`, then it will fail because the `SquarePeg` class doesn’t have a `radius` property (i.e. doesn’t satisfy the required interface).

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

    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:

    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:

    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

## 2. Inheritance Example

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

    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:

    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:

    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:

    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

## 3. Adapting using Closures

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

    interface RoundThing {
        def getRadius()
    }

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

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

And use it like this:

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

## 4. Adapting using the ExpandoMetaClass

As of Groovy 1.1, there is a built-in MetaClass which can automatically add properties and methods dynamically.

Here is how the example would work using that feature:

    def peg = new SquarePeg(width: 4)
    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.