Using The Adapter Pattern

posted on 03/28/08 at 08:00:57 pm by Joel Ross

In my last post about Dependency Injection, I sort of just threw in a comment about using the Adapter Pattern to address a mismatch between existing classes and interfaces that those classes need to support. I didn't provide a link or any real explanation. It was late when I wrote it, so I didn't really think about it. After reading through it again today, it struck me that I should probably explain it and provide an example of that as well.

Actually, this is all just a ploy to write about as many design patterns as possible to prove to someone (who shall remain un-named for now) that patterns are worth it and useful!

The basic idea of an adapter is to allow a class (or set of classes) support an interface without modifying the class or the interface. There's a a couple of reasons you'd need to do this:

  1. Your set of classes all do the same type of thing (post to a micro-blogging service, for example), but with a different API, and you want to write code to treat them all the same.
  2. You have existing code that expects a certain interface. Your new class doesn't support that interface and it's not feasible to change that (for example, it's a third party library).

That all sounds good, but examples cement the idea for me. Let's take a look at an existing class (following on my example from the DI post).

   1:  public Class TwitterAPI
   2:  {
   3:     public void SendTweet(string message)
   4:     {
   5:        // Send tweet implementation
   6:     }
   7:  }

Now, let's look at the code that we want to use this. This code already is using DI and expects a class that supports the IServiceAPI interface.

   1:  public class User
   2:  {
   3:     private IServiceAPI _service;
   4:     public IServiceAPI Service
   5:     {
   6:        get { return _service;}
   7:     }
   9:     private string _userName;
  10:     public string UserName
  11:     {
  12:        get { return _userName; }
  13:     }
  15:     public User(string userName, IServiceAPI service)
  16:     {
  17:        _userName = userName;
  18:        _service = service;
  19:     }
  21:     public void SendMessageTo(string message)
  22:     {
  23:        service.SendMessage(String.Format("@{0} - {1}", this.UserName, message));
  24:     }
  25:  }

Now, TwitterAPI doesn't support the IServiceAPI interface, so to make these two classes work together, we have a choice. We can modify the TwitterAPI class directly to support the IServiceAPI interface. But what if the TwitterAPI is a third party library or it's already in use in other places? Well, that's where the Adapter pattern comes in. We can wrap the TwitterAPI class with an adapter so it can be used in the User class. It'll look like this:

   1:  public class TwitterAPIAdapter : IServiceAPI
   2:  {
   3:     private TwitterAPI _twitter;
   4:     public TwitterAPI Twitter
   5:     {
   6:        get { return _twitter; }
   7:     }
   9:     public TwitterAPIAdapter(TwitterAPI twitter)
  10:     {
  11:        _twitter = twitter;
  12:     }
  14:     public void SendMessage(string message)
  15:     {
  16:        twitter.SendTweet(message);
  17:     }
  18:  }

Now we can pass in a TwitterAPIAdapter instance into User, and post to Twitter. The advantage of doing this is especially important for existing code bases. This allows these two disconnected pieces (User and TwitterAPI) to talk to each other but without modifying either class, meaning getting them to talk is less impactful to the overall system because neither original classes were changed.

This example is a simple one. But it does show the concept - to overcome the mismatch between the two classes and allow them to work together. In more complex situations, that may include pulling in information from other sources, pushing data to multiple locations, etc.

The adapter pattern is also a good way to encapsulate the functionality of a volatile piece of a system. For example, we have had issues with our HTML editor we use on Tourneytopia, and as a result, have changed it out a few times. The first time we did, it was painful because it's used in a few different places. The second time we changed it, it was easy because the first time, we created an adapter that sat between our application and the third party editor. Now we can swap editors out by just changing the internals of the adapter and our application never has to change.

Categories: Development, C#