Adapter Design Patterns
Lecture 21
Adapter Design Pattern
Gang of Four state the intent of Adapter is to
Convert the interface of a class into another interface that
the clients expect. Adapter lets classes work together that
could not otherwise because of incompatible interfaces.
Problem Specification:
Step 1:
You are given the following class library to draw
shapes.
Shape
+display()
+fill()
+undisplay()
Line Square
+display() +display()
+fill() +fill()
+undisplay() +undisplay()
Step 2:
Now you are asked to add another class to deal
with circle. Your plan was:
Shape
+display()
+fill()
+undisplay()
Circle
Line Square
+display() +display()
+fill() +display()
+fill() +fill()
+undisplay() +undisplay() +undisplay()
Step 3:
Then your client said: “No,no,no”. You have to use this
nice class library for drawing circles. Unfortunately, it is
from a different vendor and it has a different interface.
Shape
+display()
+fill()
AnotherCircle
+undisplay()
+setLocation()
+drawIt()
+fillIt()
+setItColor()
Line Square +unDrawIt()
+display() +display()
+fill() +fill()
+undisplay() +undisplay()
Adapter Design Pattern
Problem: An "off the shelf" component offers
compelling functionality that you would like to reuse,
but its "view of the world" is not compatible with the
philosophy and architecture of the new system
Adapter is about creating an intermediary abstraction
that translates, or maps, the old component to the new
system
Use Adapter when you need a way to create a new
interface for an object that does the right stuff but has
the wrong interface
Two kinds of adapter pattern
Object adapter pattern
The adapter class contains adaptee object
Class adapter pattern
Adapter class is inherited from both adaptee and
abstract class.
Object Adapter
AbstractClass
AdapterClass AdapteeClass
Adapter Design Pattern Solution
Object Adapter
Shape
+display()
adaptee
+fill()
+undisplay()
AnotherCircle
Line Circle
Square
+display() -aCircle:AnotherCircle +setLocation()
+display()
+fill() +drawIt()
+fill() +display()
+undisplay() +fillIt()
+undisplay() +fill() +setItColor()
+undisplay() +unDrawIt()
void display(){ void fill(){ void undisplay(){
aCircle.drawIt(); aCircle.fillIt(); aCircle.unDrawIt();
} } }
Class Adapter
AbstractClass AdapteeClass
AdapterClass
Adapter Design Pattern Solution
Class Adapter
AnotherCircle
Shape
+setLocation()
+drawIt()
+display() +fillIt()
+fill() +setItColor()
+undisplay() +unDrawIt()
Line Square Circle
+display() +display()
+fill() +fill() +display()
+undisplay() +undisplay() +fill()
+undisplay()
void display(){ void fill(){ void undisplay(){
drawIt(); fillIt(); unDrawIt();
} } }
Class Adapter Vs Object Adapter
Class Adapter
uses inheritance and can only wrap a class. It cannot
wrap an interface since by definition it must derive
from some base class.
Object Adapter
uses composition and can wrap classes or interfaces, or
both. It can do this since it contains, as a private,
encapsulated member, the class or interface object
instance it wraps.
Goal of Adapter Pattern
Keeping the client code intact we need to write a
new class which will make use of services offered
by the class.
Convert the services offered by class to the client
in such a way that functionality will not be
changed and the class will become reusable.
Flow of Events in Adapter
Pattern
Client call operations on Adaptor instance, which
in return call adaptee opertions that carry out the
request.
To use an adapter:
The client makes a request to the adapter by calling a method on it
using the target interface.
The adapter translates that request on the adaptee using the adaptee
interface.
Client receive the results of the call and is unaware of adapter’s
presence.
Implementation steps
Identify the desired interface.
Design a "wrapper" class that can "impedance
match" the old to the new.
The adapter/wrapper class "has a" instance of the
legacy class.
The adapter/wrapper class "maps" (or delegates)
to the legacy object.
The client uses (is coupled to) the new interface.
Components of Adapter Class
1. Adaptee: Defines an existing interface that needs
adapting; it represents the component with which the
client wants to interact with.
2. Target: Defines the domain-specific interface that the
client uses; it basically represents the interface of the
adapter that helps the client interact with the adaptee.
3. Adapter: Adapts the interface Adaptee to the Target
interface; in other words, it implements the Target
interface, defined above and connects the adaptee, with
the client, using the target interface implementation
4. Client: The main client that wants to get the operation, is
done from the Adaptee.
Limitations of Adapter Design
Pattern
Due to adapter class the changes are encapsulated
within it and client is decoupled from the changes
in the class.
A client in video game wants to use an enemy
attacker. Any enemy attacker can fire a weapon,
drive forward and assign driver to it. However,
you want to create an artificially intelligent enemy
robot. Now, an enemy robot has no need for a
driver and also it is not going to drive forward ,it’s
going to walk forward, and has no weapons it just
smashes things with its feet's and its hand. We
want to see enemy robot as an enemy attacker.
Step 1:Create interface for
Enemy Attacker
// This is the Target Interface : This is what the client
// expects to work with. It is the adapters job to make new
// classes compatible with this one.
public interface EnemyAttacker {
public void fireWeapon();
public void driveForward();
public void assignDriver(String driverName);
}
Step 2:
// EnemyTank implements EnemyAttacker perfectly
// Our job is to make classes with different methods
// from EnemyAttacker to work with the EnemyAttacker interface
import java.util.Random;
public class EnemyTank implements EnemyAttacker{
Random generator = new Random();
public void fireWeapon() {
int attackDamage = generator.nextInt(10) + 1;
System.out.println("Enemy Tank Does " + attackDamage + " Damage");}
public void driveForward() {
int movement = generator.nextInt(5) + 1;
System.out.println("Enemy Tank moves " + movement + " spaces");}
public void assignDriver(String driverName) {
System.out.println(driverName + " is driving the tank"); }
}
Step 3: Adaptee
// This is the Adaptee. The Adapter sends method calls
// to objects that use the EnemyAttacker interface
// to the right methods defined in EnemyRobot
import java.util.Random;
public class EnemyRobot{
Random generator = new Random();
public void smashWithHands() {
int attackDamage = generator.nextInt(10) + 1;
System.out.println("Enemy Robot Causes " + attackDamage + " Damage
With Its Hands"); }
public void walkForward() {
int movement = generator.nextInt(5) + 1;
System.out.println("Enemy Robot Walks Forward " + movement + "
spaces"); }
public void reactToHuman(String driverName) {
System.out.println("Enemy Robot Tramps on " + driverName);
}
Step 4:Adapter
// The Adapter must provide an alternative action for
// the the methods that need to be used because
// EnemyAttacker was implemented.
// This adapter does this by containing an object
// of the same type as the Adaptee (EnemyRobot)
// All calls to EnemyAttacker methods are sent
// instead to methods used by EnemyRobot
public class EnemyRobotAdapter implements EnemyAttacker{
EnemyRobot theRobot;
public EnemyRobotAdapter(EnemyRobot newRobot){
theRobot = newRobot; }
public void fireWeapon() {
theRobot.smashWithHands(); }
public void driveForward() {
theRobot.walkForward(); }
Step 4:Adapter
public void assignDriver(String driverName) {
theRobot.reactToHuman(driverName);
}
Step 5: Client
public class TestEnemyAttackers{
public static void main(String[] args){
EnemyTank rx7Tank = new EnemyTank();
EnemyRobot fredTheRobot = new EnemyRobot();
EnemyAttacker robotAdapter = new EnemyRobotAdapter(fredTheRobot);
System.out.println("The Robot");
fredTheRobot.reactToHuman("Paul");
fredTheRobot.walkForward();
fredTheRobot.smashWithHands();
System.out.println();
System.out.println("The Enemy Tank");
rx7Tank.assignDriver("Frank");
rx7Tank.driveForward();
rx7Tank.fireWeapon();
System.out.println();
Step 5: Client
System.out.println("The Robot with Adapter");
robotAdapter.assignDriver("Mark");
robotAdapter.driveForward();
robotAdapter.fireWeapon();
}
Output