>

Tuesday, 15 December 2015

Adapter

Adapter:

“Convert the interface of a class into another interface clients expect.”
Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Motivation:
Sometimes a toolkit or class library can not be used because its interface is incompatible with the interface required by an application. We can not change the library interface, since we may not have its source code. Even if we did have the source code, we probably should not change the library for each domain-specific application.

Adapter is the only design pattern which is of both the scopes ie it can be of Class scope as well as of Object scope ie Adapters in software can be implemented both using class inheritance as well as object composition.

Class Level Adapter:




In this class diagram , there are two classes representing two systems viz the SrcSystem and the TargetSystem. These are two classes have heterogeneous interfaces. Heterogenity in interfaces is depicted here by having making the SrcEntity and TargetSystem having different signatures. SrcEntity has a method request() while the TargetSystem has a method specificRequest(). There is also a class - Adapter representing the adapter. As the Adapter should know both the interfaces, one way of doing the same is by using multiple inheritance ie Making the Adapter class inherit from both the SrcEntity and the TargetSystem. As the Adapter is inheriting from the SrcEntity , it can override the request() in the Adapter class. The Adapter in turn can call the specificRequest() method of the TargetSystem.
Hence when the client invokes the request() method the request() method of the Adpater will get called. If any preprocessing needs to be done will be done and then the specificRequest() method of the TargetSystem will be called. The results from the TargetSystem is post processed and finally returned to the client. This is how adaption happens in a class level adapter.
Object Level Adapter:



The difference being , here instead of having a class called as TargetSystem, we will have an interface called as TSInterface which has a method called specificRequest(). There can be one or more implementation classes of this interface which will override the specificRequest() method providing different implementations. Also in the Adapter class instead of extending from the TargetSystem as in the class level adapter , the Adapter will now keep a reference to the TSInterface.
Hence for any request from the client the request() method of the Adapter gets called … any translations that need to be done can be performed in the preprocessing stage of the method, subsequently the specificRequest() method is invoked on one of the implementation classes. Any return results may be retranslated in the post processing stage and the result will then be returned to the Client.
What can an Object level adapter do that a Class level adapter cannot do?
1.)  A Class level adapter can adapt a single source to single target while an Object level adapter can adapt a single source to multiple targets. As Object level adapter uses Object Composition which is nothing but simple association and polymorphism , hence its gets the flexibility of Adapting a single source to multiple targets.
2.) As there is no level of indirection involved, Class level adapters gives better performance as compared to Object level adapters.
Now what can a Class level adapter do that an Object level adapter cannot do?
1.) Class level adapter if need be can override the specificRequest() method [due to multiple inheritance ] of the TargetSystem which an Object Level adapter cannot do. Object level adapter cannot do this as it just keeps a reference to the TargetSystem.
2.)Another problem with class level adapters is only C++ as a programming language supports multiple inheritance while most of the other frequently used programming languages don’t support multiple inheritance. Hence Class level adapters are mostly limited to C++ programming language.
3.)As flexibility is desirable in most of the cases, Object level adapters are more frequently used as compared to Class level adapters.

Check list:

  1. Identify the players: the component(s) that want to be accommodated (i.e. the client), and the component that needs to adapt (i.e. the adaptee).
  2. Identify the interface that the client requires.
  3. Design a “wrapper” class that can “impedance match” the adaptee to the client.
  4. The adapter/wrapper class “has a” instance of the adaptee class.
  5. The adapter/wrapper class “maps” the client interface to the adaptee interface.
  6. The client uses (is coupled to) the new interface





Example 1:
Consider a utility class that has a copy() method which can make a copy of an vector excluding those objects that meet a certain criteria. To accomplish this method assumes that all objects in the vector implement the Copyable interface providing the isCopyable() method to determine if the object should be copied or not.


Here’s the Copyable interface:
public interface Copyable {
public boolean isCopyable();
}

And here’s the copy() method of the VectorUtilities class:
public static Vector copy(Vector vin) {
Vector vout = new Vector();
Enumeration e = vin.elements();
while (e.hasMoreElements()) {
Copyable c = (Copyable) e.nextElement();
if (c.isCopyable())
vout.addElemet(c);
}
return vout;
}



But what if we have a class, say the Document class that does not implement the Copyable interface. We want to be able perform a selective copy of a vector of Document objects, but we do not want to modify the Document class at all. To make things simple, let’s assume that the Document class has isValid() method we can invoke to determine whether or not it should be copied.






public class DocumentAdapter implements Copyable {
private Document d;
public DocumentAdapter(Document d) {
     document = d;
}
public boolean isCopyable() {
      return d.isValid();
}
}


Example 2:

#include "stdafx.h"
#include <iostream>
 
using namespace std;
class ExecuteInterface {
  public:
    // 1. Specify the new interface
    virtual ~ExecuteInterface(){}
    virtual void execute() = 0;
};
 
// 2. Design a "wrapper" or "adapter" class
template <class TYPE>
class ExecuteAdapter: public ExecuteInterface {
  public:
    ExecuteAdapter(TYPE *o, void(TYPE:: *m)()) {
      object = o;
      method = m;
    }
    ~ExecuteAdapter() {
      delete object;
    }
 
    // 4. The adapter/wrapper "maps" the new to the legacy implementation
    void execute() {  /* the new */
      (object->*method)();
    }
  private:
    TYPE *object; // ptr-to-object attribute
 
    void(TYPE:: *method)(); /* the old */     // ptr-to-member-function attribute
};
 
// The old: three totally incompatible classes
// no common base class,
class Fea {
  public:
  // no hope of polymorphism
  ~Fea() {
    cout << "Fea::dtor" << endl;
  }
  void doThis() {
    cout << "Fea::doThis()" << endl;
  }
};
 
class Feye {
  public:~Feye() {
    cout << "Feye::dtor" << endl;
  }
  void doThat() {
    cout << "Feye::doThat()" << endl;
  }
};
 
class Pheau {
  public:
  ~Pheau() {
    cout << "Pheau::dtor" << endl;
  }
  void doTheOther() {
    cout << "Pheau::doTheOther()" << endl;
  }
};
 
 
/* the new is returned */
ExecuteInterface **initialize() {
  ExecuteInterface **array = new ExecuteInterface *[3];
 
  /* the old is below */
  array[0] = new ExecuteAdapter < Fea > (new Fea(), &Fea::doThis);
  array[1] = new ExecuteAdapter < Feye > (new Feye(), &Feye::doThat);
  array[2] = new ExecuteAdapter < Pheau > (new Pheau(), &Pheau::doTheOther);
  return array;
}
 
int main() {
  ExecuteInterface **objects = initialize();
  for (int i = 0; i < 3; i++) {
   objects[i]->execute();
  }
 
  // 3. Client uses the new (polymporphism)
  for (i = 0; i < 3; i++) {
    delete objects[i];
  }
  delete objects;
  return 0;
}


Example 3:
/*
* This is our adaptee, a third party implementation of a
* number sorter that deals with Lists, not arrays.
*/
public class NumberSorter
{
public List<Integer> sort(List<Integer> numbers)
{
//sort and return
return new ArrayList<Integer>();
}
}

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. 

int[] numbers = new int[]{34, 2, 4, 12, 1};
Sorter sorter = new SortListAdapter();
sorter.sort(numbers);
We've provided a Sorter interface that expects the client input. This is our target.
//this is our Target interface
public interface Sorter
{
public int[] sort(int[] numbers);
}
Finally, the SortListAdapter implements our target interface and deals with our adaptee, NumberSorter
public class SortListAdapter implements Sorter
{
public int[] sort(int[] numbers)
{
//convert the array to a List
List<Integer> numberList = new ArrayList<Integer>();

//call the adapter
NumberSorter sorter = new NumberSorter();
numberList = sorter.sort(numberList);

//convert the list back to an array and return

return sortedNumbers;
}
}




0 comments:

Post a Comment