Intro to Java Adapter Pattern

In the last lesson we looked at patterns in general and why we might want to use them. So here is our first pattern. We are well on our way to creating a common language with which to talk design.

One of the reasons I picked this pattern to start with, is that it is very easy to understand. It models real word situations very well, and that is what OO design is supposed to do right, model real world objects?

The Adapter in the Physical World

The concept of an Adapter is easy to understand. It does just what it is named to do. It adapts one object to that of another. Take the wall plug. It has three rectangular holes set at angles to fit the plug. It does? In Europe it does. We are traveling and need to plug our laptop into the wall. What do we use? An adapter.

The Adapter in the Software World

The idea is not so different in the software world. When you want to connect one class to another class that was not designed to fit together, you need an adapter class. Let’s look at an Java example to see how we can adapt one class to another.

Adapting an Animal

In the last few lessons we have been dealing with the Animal interface. This is what it looked like:

public interface Animal {

   public void speak();

   public void move();

   public String getName();

   public void setName(String pName);

   public String getType();
}

We also had a BaseAnimal, but the idea was we were coding to this interface. So, keep in mind the speak() and move() methods. All the animals that we created implement these methods. What if we have a class that doesn’t implement Animal, but we want to treat as an Animal? For example, what if we found a really cool Reptile class out on the internet that we really wanted to use as an Animal, but it doesn’t implement our Animal interface. It doesn’t have speak() and move() methods. It has more specific methods hiss() and slither().

public class Reptile {

   public final void slither() {
      System.out.println("Reptile slithers.");
   }

   public final void hiss() {
      System.out.println("Reptile hisses.");
   }

   public final void snap() {
      System.out.println("Reptile snaps.");
   }
}

Options?

What can we do? What are our options? We could modify the code to have it implement the interface and change the method names to match our animal interface. That is probably a pattern that you have seen before, but it isn’t usually the best option.

What happens if the supplier, or coder of Reptile updates their code? Say, adds some code that improves the slithering? Every time you would have to install that class overtop of your changes, and then modify it again. That’s a lot of work. It usually means that upgrades don’t happen.

What happens if the supplier, or coder of Reptile only distributes a binary class? Are we SOL? Nope, Adapter to the rescue.

Take a look at this ReptileAnimalAdapter class:

public class ReptileAnimalAdapter extends BaseAnimal {

   private Reptile reptile;

   public ReptileAnimalAdapter() {
      super();
      reptile = new Reptile();
   }

   public String getType() {
      return "reptile";
   }

   public void move() {
      reptile.slither();
   }

   public void speak() {
      reptile.hiss();
   }
}

What we have done, is use containment. The ReptileAnimalAdapter contains a Reptile. We don’t make any changes to Reptile, we just use it. Notice that it extends BaseAnimal, which implements Animal. Therefore, this Adapter meets all the requirements of our Animal interface contract. Look at the methods here. The ones we need to implement in here to finish the contract are getType(), move(), and speak(). We don’t change any methods on Reptile, we just adapt them to the Animal interface. When move is called, we call reptile.slither().

Let’s look at a class diagram of what we have now :

Animal Adapter Class Diagram

adapter class diagram

This diagram shows that a Reptile is contained within the ReptileAnimalAdapter that is extending the BaseAnimal class that is implementing the Animal interface.

Does It Work?

Take a look at AdapterTest1 to see how Reptile and ReptileAnimalAdapter work. This also shows how a ReptileAnimalAdapter “is a” Animal.

/**
 * Test to look at our ReptileAnimalAdapter and see how it works. This will show
 * that we now have a "new" Animal type.
 *
 * @author Chris Ward 
 */
public class AdapterTest1 {

   public static void main(String[] args) {

      // Show what a Reptile looks like
      Reptile aReptile = new Reptile();
      aReptile.slither();
      aReptile.hiss();
      // note: see that move is not a method on Reptile by uncommenting this
      // aReptile.move();

      // Show that the ReptileAnimalAdapter can be used on it's own, and that
      // it no longer has the "slither" method.
      ReptileAnimalAdapter aAdapter = new ReptileAnimalAdapter();
      aAdapter.move();
      aAdapter.speak();
      // note: see that slither is not a method on the adapter by uncommenting this
      // aAdapter.slither();

      // Show that we now have a new animal that works just like any other Animal
      Animal aAnimal = new ReptileAnimalAdapter();
      aAnimal.move();
      aAnimal.speak();
   }
}

AdapterListTest1 also shows how ReptileAnimalAdapter “is a” Animal, by putting it in a Java5 Animal Typed list.

/**
 * This test just shows that we can take our ReptileAnimalAdapter and add it to
 * a list of Animals, and use them as if they were simple Animals that move and speak.
 *
 * @author Chris Ward 
 */
public class AdapterListTest1 {

   public static void main(String[] pArgs) {

      // using Java 5 typed list. Only objects of type
      // Animal can go in this list.
      List animalList = new ArrayList();

      // if we don't need references to the animals
      // we create, don't create local variables for them.
      animalList.add(new Bird());
      animalList.add(new Fish());
      animalList.add(new ReptileAnimalAdapter());

      System.out.println("Moving animals:");
      // use a Java 5 "for each" loop to minimize typing.
      // using Java 5 typed lists we don't need casting.
      for (Animal aAnimal : animalList) {
         // we don't need to know what type of animal
         // we are moving, just that we want them to move.
         aAnimal.move();
      }

      System.out.println("Getting animals to speak:");
      // we don't need to know what type of animal
      // is speaking, just that we want them to speak.
      for (Animal aAnimal : animalList) {
         aAnimal.speak();
      }
   }
}

Review what we did.

So, what have we done with this adapter? Nothing new really. We have used some of the Java principles that we have talked about in a particular way, and called it applying the Adapter pattern. That is all that using patterns is.

  1. We created a new class (ReptileAnimalAdapter) that extends a base class, or implements an interface, (BaseAnimal in this case) and call it an Adapter.
  2. We used containment to hold an instance of the class we need to adapt. (Reptile)
  3. We coded to the interface (of Animal), to adapt the methods of Animal to Reptile.

That’s it. Nothing too complicated or convoluted. Some of the patterns are more complicated than this one, but several are simple. The point, is that you now understand a pattern. The next step is learning to recognize the pattern when you see it, and learning when to use it when presented with a problem.

Now when someone says, “Hey, I found a cool FlyingFish class! Can we use it with our code?”. You can respond with “Sure, we just need an Adapter class!”

Series NavigationIntro to Java PatternsIntro to Java Singleton Pattern

5 thoughts on “Intro to Java Adapter Pattern

  1. Chris,

    I’m currently studying a software engineering module as part of my masters. A big part of that module has been Patterns, naturally. I’m currently going through a list of about a dozen
    patterns or so, and I have been finding it frustrating to find neat, tidy, concise and accessible explanations for each of the pattern types. In short, I’m saying that your explanations are excellent. Basically you have excellent pedagogy. It never fails to amaze me how so many people out there manage to make a relatively simple concept so difficult to understand.

    Anyway, thanks. You’re making my finals a whole lot easier….!

    Mal

Comments are closed.