Design pattern: Adapter
Intent
- Changes interfaces of a class to support other functions.
Explain
Adapter pattern is a good way to reuse existing classes. If stack is required, there are two ways to provide it. The first way is to make stack from scratch. However, it might take a lot of effort and time. Also, after making stack, it is necessary to verify the functionality of stack. The second way is to modify existing list or deque data structure. List and deque have different policy to store and load data from stack. Therefore, it is possible to change the policy, they can be stack. Code 1 shows stack from list.
// 01_C_testStackFromList.cpp
#include <iostream>
#include <list>
using namespace std;
// Prohibits the original functions of list
template <typename T>
class Stack : private list<T>
{
public:
inline void push(const T& a)
{
list<T>::push_back(a);
}
inline void pop()
{
list<T>::pop_back();
}
inline T& top()
{
return list<T>::back();
}
inline int size()
{
return list<T>::size();
}
// Typename let compile know list<T> is type
inline typename list<T>::iterator begin()
{
return list<T>::begin();
}
inline typename list<T>::iterator end()
{
return list<T>::end();
}
};
int main()
{
Stack<int> stack;
stack.push(10);
stack.push(20);
stack.push(30);
int i = 0;
//stack.push_front(10); // push_front is forbidden
for (auto num : stack)
{
cout << "Index: " << i++ << ", Value: " << num << endl;
}
cout << "Size at start: " << stack.size() << endl;
int size = stack.size();
for (int i=0; i<size; i++)
{
// Get a value of top
int top = stack.top();
cout << "Pop: " << top << ", remain: " << stack.size() << endl;
// Remove top value
stack.pop();
}
return 0;
}
// Compile: clang++ -std=c++14
// -o 01_C_testStackFromList 01_C_testStackFromList.cpp
Index: 0, Value: 10
Index: 1, Value: 20
Index: 2, Value: 30
Size at start: 3
Pop: 30, remain: 3
Pop: 20, remain: 2
Pop: 10, remain: 1
This stack inherits list as private, and extends push(), pop(), and top(). Also, stack overrides some functions. The reason why private is used is to hide interfaces of list. It is important to prevent a user from using a class in wrong way. If all interfaces are exposed, stack can be destroyed by push_front(). Private, and protected hide interfaces, but allows to use implementation, so it is useful for adapter pattern.
Alternatively, composition is better option than inheritance for adapter pattern. It can also hide interfaces, and allow to use implementation. Furthermore, Composition makes loosely coupling between a parent and a child class. Also, composition allows having a multiple variables from the same class. Code 2 shows how stack is implemented with composition in C++ STL.
// 02_C_StackByComposition.cpp
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm> // For for_each
using namespace std;
template <typename T1, typename T2=deque<T1>>
class Stack
{
T2 container;
public:
inline void push(const T1& a)
{
container.push_back(a);
}
inline void pop()
{
container.pop_back();
}
inline T1& top()
{
return container.back();
}
inline int size()
{
return container.size();
}
inline typename T2::iterator begin()
{
return container.begin();
}
inline typename T2::iterator end()
{
return container.end();
}
};
void show(int itr)
{
cout << itr << endl;
}
int main()
{
Stack<int> dStack; // Deque base stack
dStack.push(10);
dStack.push(20);
dStack.push(30);
cout << "Stack from Deque" << endl;
for_each(dStack.begin(), dStack.end(), show);
Stack<int, vector<int>> vStack; // Vector base stack
vStack.push(10);
vStack.push(20);
vStack.push(30);
cout << "Stack from Vector" << endl;
for_each(vStack.begin(), vStack.end(), show);
}
// Compile: clang++ -std=c++14
// -o 02_C_StackByComposition 02_C_StackByComposition.cpp
Stack from Deque
10
20
30
Stack from Vector
10
20
30
Summary
- Adapter pattern helps to make new class from existed class.
- Adapter pattern can be implemented by Composition or private (or protected) inheritance.
COMMENTS