Adapter Design Pattern
Intent
The Adapter Design Pattern
Intent
Intent: Fit foreign components into an existing design.

We want to reuse existing frameworks or libraries in our software, even if they do not match with our design.
We do not want to change our design to adhere to the structure of the reused components.
Case Study
The Adapter Design Pattern - Illustrated

We have acquired the framework GraphicalFramework
.
GraphicalFramework
provides the interface Node
to draw rectangles with a headline and text to the screen.
Drawing is done by the framework, we just need to provide the data via the interface Node
.
Desired Usage of the Framework

Our own design represents different kinds of persons.
We want to draw our data to the screen:
- Name and department of
Employee
.
- Name and address of
Customer
.
Adapting the Framework

We will create adapters to use the functionality of GraphicalFramework
for our classes.
We have to adapt Employee
and Customer
to fit with Node
.
Two Kinds of Adapters
Object Adapter
Object Adapter

Adaptee
is wrapped by Adapter
to fit in the interface of Target
.
Adapter
forwards calls of Client
to request()
to the specific methods of Adaptee
(e.g, specificRequest()
).
Using Object Adapter

Advantages:
Adapter
works with Adaptee
and any subclass of it.
Adapter
can add functionality to Adaptee
and its subclasses.
Disadvantages:
- Cannot override methods in
Adaptee
.
- Cannot reuse
Adapter
with subclasses of Target
.
Adapter
and Adaptee
are different objects.
(Need to maintain relation between Adaptee
and his Adapter
)
Class Adapter
Class Adapter

Instead of having Adaptee
as an attribute, Adapter
inherits from Adaptee
.
Using Class Adapter

Advantages:
- Behavior of
Adaptee
can be overridden.
- Adapter and
Adaptee
are the same object, no forwarding.
Disadvantages:
Adapter
cannot be used with subclasses of Adaptee
or Target
.
Multiple
inheritance may be required.
In Java: At least one of Target
and Adaptee
must be an Interface.
Takeaway
Takeaway
Adapter is an effective means to adapt existing behavior to the expected interfaces of a reusable component or framework.
Two variants: Object and Class Adapter
- Both have their trade-offs.
- Both have problems with the reusability of the adapter.
Pimp My Library (Scala)
Pimp-my-Library Idiom/Pattern (Scala)
Goal
Solve the problem that you can change or extend your own code, but if you use other libraries you have to take them as they are.
Solution Idea
Define a conversion function to convert your object into the required object and make this conversion implicit
to let the compiler automatically perform the conversion when needed.
(Transparent generation of object adapters.)
Example Scenario
We want to be able to repeat a certain operation multiple times and want to store the result in some given mutable store.
But, Scala's (2.10) mutable collections do not define a common method to add an element to them.
In the following we develop a generalization of the previously shown repeat
method. This variant enables the developer to specify the target data store.
Implementing a repeatAndStore
method (naïve approach).
object ControlFlowStatements {
import scala.collection.mutable.Set
abstract class MutableCollection[T, C[T]](val underlying: C[T]) {
def +=(elem: T): Unit
}
implicit def setToMutableCollection[T](set: Set[T]) =
new MutableCollection(set) { def +=(elem: T) = set += (elem) }
def repeatAndStore[T, C[T]]
(times: Int)(f: ⇒ T)(collection: MutableCollection[T, C]): C[T] = {
var i = 0; while (i < times) { collection += f; i += 1 }
collection.underlying
}
}
The previous solution has two issues:
- The
repeatAndStore
method requires a MutableCollection
which is basically an implementation-internal type.
- It returns the original collection to make the usage easier, but important type information is lost (the
HashSet
has become a Set
).
Implementing a repeatAndStore
method.
object ControlFlowStatementsBase {
trait Mutable[-C[_]] {
def add[T](collection: C[T], elem: T): Unit
}
implicit object Set extends Mutable[Set] {
def add[T](collection: Set[T], elem: T) { collection += elem }
}
implicit object MutableBuffer extends Mutable[Buffer] {
def add[T](collection: Buffer[T], elem: T) { collection += elem }
}
def repeatWithContextBound[T, X[T] <: AnyRef: Mutable]
(times: Int)
(f: ⇒ T)(collection: X[T]): collection.type = {
var i = 0
while (i < times) {
implicitly[Mutable[X]].add(collection, f); i += 1
}
collection
}
}