Thursday, September 20, 2012

Adapter Design Pattern

Adapter Design Pattern:
 
Software usually consists of a mixture of in-house and purchased software that must work together to produce a seamless user interface. But disparate software packages are not aware of each other's object models, so they can't work together—without adapters. Adapters let objects from unrelated software packages collaborate by adapting one interface to another. Learn how the Adapter design pattern can save you a lot of time and effort by combining disparate software systems.

The Adapter pattern lets disparate object types work together.

Introducing Adapter

Adapters are necessary because dissimilar elements need to interoperate. From wrenches to computer networks, physical adapters are abundant. In software, adapters make dissimilar software packages work together; for example, you might have a tree of objects (call them Nodes) you want to display using Swing's JTree. The JTree class can't display your Nodes directly, but it can display TreeNode instances. With an adapter, you can map your Nodes to TreeNodes. Because Swing trees use the Adapter pattern, you can display any kind of tree—from Document Object Model (DOM) to Swing component hierarchies to a compiler parse tree—just by implementing a simple adapter.


In Design Patterns, the authors describe the Adapter pattern like this:
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Figures 2 and 3 show the Adapter pattern's two standard variations.

Figure 2. Adapter with inheritance. Click on thumbnail to view full-size image.


Figure 3. Adapter with delegation. Click on thumbnail to view full-size image.

Adapters masquerade as one type of object by implementing its interface; they inherit (or delegate) functionality from another class. That way, you can effectively substitute objects (known as Adaptees) for target (Target) objects. For the JTree in Figure 1, I adapt the objects in the tree (UIComponents) to Swing, so a JTree instance can manipulate them. Considering the amount of code that sits behind JTree, this simple adapter provides a huge return on investment. Let's see how it works.

In Eclipse:

This pattern is used a lot in Eclipse, allowing plug-ins to be loosely coupled, yet still be integrated into the Eclipse runtime.


Adapters in the Real World 

A real world analogy always helps with the understanding of a design pattern. The best example for the adapter pattern is based around AC power adapters. Say you're visiting Europe from the US, with your laptop, which expects a US power supply. To get your laptop plugged in, you're going to need to get a power adapter that accepts your US plug and allows it to plug in to the European power outlet. The AC adapter knows how to deal with both sides, acting as a middleman - this is the adapter pattern.

The Adapter Pattern

The Adapter is known as a structural pattern, as it's used to identifying a simple way to realize relationships between entities. The definition of Adapter provided in the original Gang of Four book on Design Patterns states: 
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Let's take a look at the classic diagram definition of  the adapter pattern:

The Target interface defines the domain specific interface that the Client used, so the client collaborates with objects that implement the Target interface. On the other side of things, the Adaptee is the existing interface that needs adapting in order for our client to interact with it. The Adapter adapts the Adaptee to the Target interface - in other words, it translates the request from the client to the adaptee.


Let's take a look at the interactions in a sequence diagram: 

In this example, as far as the Client is concerned it's just calling the request method of the Target interface, which the Adapter has implemented. In the background however, the Adapter knows that to return the right result, it needs to call a different method, specificAdapteeRequest, on the Adaptee.
Note: the pattern described here is the object adapter. There is a class adapter pattern, but you need multiple inheritance to use it. Seeing as Java doesn't support multiple inheritance, I'm going to leave this out.

Where Would I Use This Pattern?

The main use of this pattern is when a class that you need to use doesn't meet the requirements of an interface. As mentioned before, adapters are common across Eclipse plug-ins. For a particular object to contribute to the Properties view, adapters are used display the objects data. The view itself doesn't need to know anything about the object the it is displaying properties for. 

Where Would I Use This Pattern?

The main use of this pattern is when a class that you need to use doesn't meet the requirements of an interface. As mentioned before, adapters are common across Eclipse plug-ins. For a particular object to contribute to the Properties view, adapters are used display the objects data. The view itself doesn't need to know anything about the object the it is displaying properties for. 

So How Does It Work In Java?

The following example shows a simple implementation of the pattern. Consider that we have a third party library that provides sorting functionality through it's NumberSorter class. This is our Adaptee.
01./*
02.* This is our adaptee, a third party implementation of a
03.* number sorter that deals with Lists, not arrays.
04.*/
05.public class NumberSorter
06.{
07.public List<Integer> sort(List<Integer> numbers)
08.{
09.//sort and return
10.return new ArrayList<Integer>();
11.}
12. 
13.}
Our Client deals with primitive arrays rather than Lists. For the sake of this example, lets say we can't change the client to use Lists. 
1.int[] numbers = new int[]{34, 2, 4, 12, 1};
2. 
3.Sorter sorter = new SortListAdapter();
4.sorter.sort(numbers);
We've provided a Sorter interface that expects the client input. This is our target.
1.//this is our Target interface
2.public interface Sorter
3.{
4.public int[] sort(int[] numbers);
5.}
Finally, the SortListAdapter implements our target interface and deals with our adaptee, NumberSorter

01.public class SortListAdapter implements Sorter
02.{
03. 
04.@Override
05.public int[] sort(int[] numbers)
06.{
07.//convert the array to a List
08.List<Integer> numberList = new ArrayList<Integer>();
09. 
10.//call the adapter
11.NumberSorter sorter = new NumberSorter();
12.numberList = sorter.sort(numberList);
13. 
14.//convert the list back to an array and return
15. 
16.return sortedNumbers;
17.}
18. 
19.}
While this example may be overkill, it illustrates how the adapter pattern can work.

Watch Out for the Downsides

Some say that the Adapter pattern is just a fix for a badly designed system, which didn't consider all possibilties. While this is a fair point, it is an important part of a pluggable architecture.  It can also add a level of complexity to your code, making debugging more difficult.



No comments:

Post a Comment