Adapter

Other Names

Wrapper

Problem

An object is available that provides the services needed by a client, but the expected interface does not match, i.e. the prototypes of the object's member functions are not the same as the functions called by the client object.

Solution

Create an adapter object that implements the interface required by the client using the member functions provided by the server object. In this context the server object is called the adaptee.

Static Structure

The adapter can access the services of the adapteee either by private inheritance:

(in this case the adapter can simply call the services inherited from the adaptee:)

void Adapter::serviceA() { serviceX(); }
void Adapter::serviceB() { serviceY(); }
 

or by delegation:

In this case the adapter must contain an adaptee or a pointer to an adaptee, and explicitly delegate to it:

void Adapter::serviceA() { myAdaptee->serviceX(); }
void Adapter::serviceB() { myAdaptee->serviceY(); }

 

Example

Each Windows window has a message queue, where Windows places user input messages such as "left mouse button down" or "arrow key pressed". Messages can also be placed there by controls, such as "OK button clicked" or "menu item selected".

Assume the following directives:

#include <time.h> // from the VC++ runtime library
#include <iostream>
using namespace std;
The format of a Windows message is similar to: typedef struct
{
   int hWnd;         // message target handle
   int message;      // message identifier, e.g. WM_PAINT
   time_t qtime;      // queueing time
} MSG;
Here's a little utility for printing messages: ostream& operator<<(ostream& os, MSG msg)
{
   cout << "hWnd = " << msg.hWnd << '\n';
   cout << "message = " << msg.message << '\n';
   cout << "time = " << msg.qtime << '\n';
   return os;
}
The services provided by a message queue can be expressed as an abstract class: class MSGQueue
{
public:
   virtual bool GetMessage(MSG* lpMsg, int hWnd) = 0;
   virtual bool PeekMessage(MSG* lpMsg, int hWnd) = 0;
   virtual void PostMessage(int hWnd, int Msg) = 0;
};

 

The standard C++ library provides a queue template. Unfortunately, whoever named the member functions must have been sleep walking. push() is used instead of enqueue() and pop() is used instead of dequeue()! No matter, we need to create an adapter anyway: #include <queue>
using namespace std;

class MSGQueueImpl1: public MSGQueue, public queue<MSG*>
{
public:

   bool GetMessage(MSG* lpMsg, int hWnd)
   {
      if (empty()) return false;
      MSG* temp = front(); // top() fell out of VC++ 5.0!
      if (temp->hWnd != hWnd) return false;
      lpMsg = temp;
      pop();
      return true;
   }

   bool PeekMessage(MSG* lpMsg, int hWnd)
   {
      if (empty()) return false;
      MSG* temp = front();
      if (temp->hWnd != hWnd) return false;
      lpMsg = temp;
      //pop();
      return true;
   }

   void PostMessage(int hWnd, int Msg)
   {
      MSG* msg = new MSG();
      msg->hWnd = hWnd;
      msg->message = Msg;
      time(&(msg->qtime));
      push(msg);
   }
};

 

Here's a second implementation based on delegation: class MSGQueueImpl2: public MSGQueue
{
public:

   bool GetMessage(MSG* lpMsg, int hWnd)
   {
      if (messages.empty()) return false;
      MSG* temp = messages.front(); // top() fell out of VC++!
      if (temp->hWnd != hWnd) return false;
      lpMsg = temp;
      messages.pop();
      return true;
   }

   bool PeekMessage(MSG* lpMsg, int hWnd)
   {
      if (messages.empty()) return false;
      MSG* temp = messages.front();
      if (temp->hWnd != hWnd) return false;
      lpMsg = temp;
      //pop();
      return true;
   }

   void PostMessage(int hWnd, int Msg)
   {
      MSG* msg = new MSG();
      msg->hWnd = hWnd;
      msg->message = Msg;
      time(&(msg->qtime));
      messages.push(msg);
   }
private:
   queue<MSG*> messages;
};

 

Here's some sample client code: int main()
{
   //MSGQueue* q = new MSGQueueImpl1();
   MSGQueue* q = new MSGQueueImpl2();
   MSG msg;

   q->PostMessage(42, 35);
   q->PostMessage(15, 98);
   q->PostMessage(93, 12);

   if (q->PeekMessage(&msg, 42))
      cout << msg;
   if (q->GetMessage(&msg, 42))
      cout << msg;
   if (q->GetMessage(&msg, 15))
      cout << msg;

   return 0;
}

Examples

Actually, the queue template in the standard template library is already an adapter for deques or lists:

template <class T, class C = deque<T> >
class queue
{
protected:
   C c; // adaptee
public:
   typedef C container_type;
   void pop() { c.pop_front(); }
   void push(const T& s) { c.push_back(s); }
   T& top() { return c.front(); }
   // etc.
};
Adapters are also extensively used in Java's AWT.

Problem

Here's a simple program that can be used to compute the average waiting times for a business in which no two customers overlap. It subtracts, adds, reads, writes, and casts objects belonging to a Time class:

#include "time.h"

int main()
{
   Time arrived, served, total;
   char response;
   int customers = 0;
   bool more = true;

   while(more)
   {
      cout << "enter arrival time: ";
      cin >> arrived;
      cout << "you entered: " << arrived << '\n';
      customers += 1;
      cout << "enter served time: ";
      cin >> served;
      cout << "you entered: " << served << " minutes\n";
      Time diff = served - arrived;
      cout << "difference = " << int(diff) << '\n';
      total = total + diff;
      cout << "total = " << int(total) << " minutes\n";
      cout << "More? (y/n): ";
      cin >> response;
      more = response == 'y' || response == 'Y';
   }

   cout << "total = " << int(total) << " minutes\n";
   cout << "average = " << double(total)/double(customers);
   cout << " minutes\n";

   return 0;
}

A Minutes class is available that represents time as the number of minutes elapsed since midnight. Unfortunately, its member functions don't have the right interface for the client: #include <iostream>
using namespace std;

class Minutes
{
public:
   Minutes(int m = 0) { minutes = m; }
   istream& read(istream& is = cin);
   ostream& write(ostream& os = cout) const;
   Minutes sub(const Minutes& m) const;
   Minutes add(const Minutes& m) const;
   int toInt() { return minutes; }
   double toDouble() { return double(minutes); }
private:
   int minutes; // elapsed minutes since midnight
};

 

#include <string>
using namespace std;

#include "minute.h"

// input format: HH:MM AM/PM
istream& Minutes::read(istream& is /* = cin */)
{
   int h; // elapsed hours
   int m; // elapsed minutes
   char c; // colon
   string apm; // am or pm

   is >> h;
   if (is.fail())
   {
      is.sync();
      return is;
   }
 
   is >> c >> m;
 
   if (c != ':' || is.fail())
   {
      is.sync();
      is.setstate(ios::failbit);
      return is;
   }

   is >> apm;
   if (apm == "pm" || apm == "p.m." ||
         apm == "PM" || apm == "P.M.")
      if (h != 12) h += 12;
   else if (apm == "am" || apm == "a.m." ||
         apm == "AM" || apm == "A.M.")
      if (h == 12) h = 0;
   else
   {
      is.sync();
      is.setstate(ios::failbit);
      return is;
   }

   minutes = h * 60 + m;
   return is;
}

 

ostream& Minutes::write(ostream& os /* = cout */) const
{
   string colon;
   int h = minutes/60;
   int m = minutes - h * 60;
   if (m < 10) colon = ":0"; else colon = ":";

   if (h == 0)
      os << 12 << colon << m << "AM";
   else if (h > 12)
      os << h - 12 << colon << m << "PM";
   else if (h == 12)
      os << h << colon << m << "PM";
   else
      os << h << colon << m << "AM";
   return os;
}

 

Minutes Minutes::add(const Minutes& m) const
{
   return Minutes((minutes + m.minutes) % (24 * 60));
}

 

Minutes Minutes::sub(const Minutes& m) const
{
   return Minutes(minutes - m.minutes);
}

Implement Time as an adapter for minutes in two ways: