blob: 0eb2c2192fca341324d73aaf9596106404555097 [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
//////////////////////////////////////////
= Adapter Pattern
The http://en.wikipedia.org/wiki/Adapter_pattern[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.
== Delegation Example
Suppose we have the following classes:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_delegation_classes,indent=0]
----
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:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_delegation_code,indent=0]
----
We can use the adapter like this:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_delegation_usage,indent=0]
----
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
----
== Inheritance Example
Let's consider the same example again using inheritance. First, here are the original classes (unchanged):
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_inheritance_classes,indent=0]
----
An adapter using inheritance:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_inheritance_code,indent=0]
----
Using the adapter:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_inheritance_usage,indent=0]
----
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
----
== Adapting using Closures
As a variation of the previous examples, we could instead define the following interface:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_closure_interface,indent=0]
----
We can then define an adapter as a closure as follows:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_closure_define,indent=0]
----
And use it like this:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_closure_usage,indent=0]
----
== 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:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=adapter_expando_meta_class,indent=0]
----
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.