The Adapter Pattern
You may be called upon to use an object in such a way that defies the expectations of either the object in question or the system you are developing. There are a number of reasons this might be the case:
- The object's interface differs greatly from that of other, similar objects in your system.
- The object's interface is way more complicated than you have any need for.
- I want to limit the scope for required changes if I replace this object with a different one in the future.
You can solve this by creating an adapter that provides a more agreeable interface between the object in question and the rest of the system.
Let's take for example a system that concerns itself with the accurate measurement of animals.
class Slug
def length_in_millis
37
end
end
class Antelope
def length_in_millis
2400
end
end
As luck would have it, some friendly American programmers/naturalists have written their own package for their own system that concerns itself with the accurate measurement of animals. They've kindly given us access to it so we can make use of it in our application.
class GrizzlyBear
def length_in_inches
78.7
end
end
Oh. They've given us access to their animal classes, but it's all in Imperial. Well, there are a couple of things we can do here. We can create an adapter class that allows us to decorate an animal with the desired interface:
class AnimalAdapter
MILLIMETRES_IN_AN_INCH = 25
def initialize(animal)
@animal = animal
end
def length_in_millis
@animal.length_in_inches * MILLIMETRES_IN_AN_INCH
end
end
bear = GrizzlyBear.new
adapted_bear = AnimalAdapter.new(bear)
adapted_bear.length_in_millis
#=> 1967.5
We can extend the class directly to layer our desired interface over existing functionality:
class AdaptedBear < GrizzlyBear
MILLIMETRES_IN_AN_INCH = 25
def length_in_millis
length_in_inches * MILLIMETRES_IN_AN_INCH
end
end
bear = AdaptedBear.new
bear.length_in_millis
#=> 1967.5