Department of Software Engineering
Mehran University of Engineering and Technology, Jamshoro
SWE411 – SOFTWARE DESIGN AND ARCHITECTURE
Instructor Ms. Afifah Jalbani Practical/Lab No. 08
Date 14-03-2025 CLOs 3
Signature Assessment Score 1 Mark
Topic
Objectives - To implement Adapter Pattern
Lab Discussion: Theoretical concepts and Procedural steps
Theory:
Adapter Pattern
The Adapter Design Pattern falls under the category of Structural Design Patterns. The Adapter
Design Pattern is used to convert an interface that a client expects into a different interface that the
existing class (Adaptee) provides. This pattern enables two incompatible interfaces to work together
without modifying the existing code.
Intent: Convert the interface of a class into another interface clients expect. Adapter lets classes
work together that couldn't otherwise because of incompatible interfaces.
Also known as: Wrapper.
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 system
currently being developed.
Scenario:
"Off-the-shelf" component: This refers to a pre-built, ready-to-use component or library
that provides specific functionality. It’s already developed, tested, and might be an external
dependency or part of a third-party service.
Compelling functionality: The functionality provided by this component is exactly what
your system needs, making it attractive to reuse.
"View of the world" is not compatible: This means that the interface, data formats, or
architectural approach of the off-the-shelf component do not match how your system works.
This is where the problem arises. The component has its own method of interacting with
other systems, which differs from what your system expects.
Motivation
The adapter pattern is adapting between classes and objects. Like any adapter in the real
world, it is used to be an interface, a bridge between two objects. In real world we have adapters for
power supplies, adapters for camera memory cards, and so on. Probably everyone has seen some
1
adapters for memory cards. What about software development? It's the same. Can you imagine a
situation when you have some class expecting some type of object and you have an object offering
the same features, but exposing a different interface? Of course, you want to use both of them so you
don't to implement again one of them, and you don't want to change existing classes, so why not
create an adapter.
Applicability
Use the Adapter pattern when
◦ You want to use an existing class, and its interface does not match the one you need.
◦ you want to create a reusable class that cooperates with unrelated or unforeseen classes, that
is, classes that don't necessarily have compatible interfaces.
◦ (Object adapter only) you need to use several existing subclasses, but it's impractical to adapt
their interface by subclassing every one. An object adapter can adapt the interface of its
parent class.
Structure
This diagram represents the class structure of the Adapter Design Pattern in UML.
1. Client:
The Client interacts with the Target interface, not directly with the Adaptee.
It sends requests through the Target interface, expecting that the class implementing this
interface will handle the request. The Client is unaware that there is an Adaptee behind the
scenes.
2. Target (Request):
The Target is an abstract class or interface that defines the method the Client uses (in this
case, Request()).
The Client works with this Request() method, without needing to know about the internal
structure or implementation.
Target could be something the Client expects based on its business logic or requirements.
3. Adaptee (SpecificRequest):
The Adaptee is the existing class that has a different interface that the Client doesn’t work
with directly.
2
It has a method, called SpecificRequest() in this diagram, which performs a specialized
function that the Client needs. However, the Client cannot use this method directly because
it doesn’t match the Target interface.
The Adaptee represents existing or legacy functionality that needs adapting to the Target.
4. Adapter (Request):
The Adapter implements the Target interface (e.g., it implements Request()).
Inside the Adapter, it translates or adapts the Request() call from the Client to something
that the Adaptee can handle, namely the SpecificRequest() method.
The Adapter bridges the gap between the Client and the Adaptee, making the Request()
compatible with SpecificRequest(). It calls the Adaptee’s SpecificRequest() and
handles the necessary conversions.
Flow in the Diagram:
1. Client → Target (Request): The Client sends a request through the Target interface by
calling Request().
2. Target → Adapter: The Adapter implements the Request() method. When the Client
calls Request(), it is forwarded to the Adapter.
3. Adapter → Adaptee (SpecificRequest): The Adapter translates the Request() into a call
to the Adaptee's SpecificRequest(), which performs the actual action.
4. Adaptee completes the operation by executing SpecificRequest().
Participants
1. Target: The Target is an interface or abstract class that defines the functionality required by
the client. It specifies the operations that the client expects to perform, typically in a format
that is convenient or relevant to the domain the client is working in.
Role: The Target defines the structure and methods that the client can rely on. It establishes
what the client can do, but it does not necessarily match the capabilities or structure of the
Adaptee.
Purpose: The target acts as the middle layer between the client and the actual
implementation (the Adaptee) by defining the methods that the client is expecting to use.
2. Client: The Client is the entity that needs to interact with the system. The client expects
objects to conform to the Target interface and uses the Target to interact with the system.
Role: The client doesn’t care about the internal workings of how the system achieves its
objectives. It only relies on the Target interface to perform actions. The Client knows
nothing about the Adaptee or its incompatibilities; it simply sends requests through the
Target interface.
Purpose: The client works in a specific domain and requires certain functionality. The
Adapter pattern ensures that the client can use incompatible objects without knowing that
they are incompatible by adapting them.
3
3. Adaptee: The Adaptee is an existing class or set of classes that contains some useful
functionality, but its interface is incompatible with the client's expected Target interface. It
provides the core functionality but in a way that the client cannot directly use.
Role: The Adaptee represents an existing implementation that may have been designed
without the current client’s needs in mind. It often operates in a different context, has a
different method signature, or uses different data formats.
Purpose: The Adapter pattern takes the Adaptee's functionality and "adapts" it to be
compatible with the Target interface, allowing the client to use it without knowing of its
internal workings.
4. Adapter: The Adapter is the key player in the pattern. It implements the Target interface and
contains an instance of the Adaptee. The Adapter translates the client's calls to the Target
interface into calls to the Adaptee's interface. Essentially, it converts the input or structure
expected by the client into a form that the Adaptee understands and vice versa.
Role: The Adapter implements the Target interface that the client expects, but under the
hood, it redirects these calls to the Adaptee by converting inputs, outputs, or method
signatures as necessary.
Purpose: The Adapter makes it possible for the client to use the Adaptee's functionality even
though their interfaces are incompatible. It acts as a translator or mediator between the two.
How They Work Together:
1. Client → Target Interface:
The client only knows about and interacts with the Target interface. In this case, the client
interacts with the PrintableList interface.
2. Target → Adapter:
The adapter implements the Target interface, so the client believes it is interacting with the
Target. However, the adapter internally translates the calls into something the Adaptee can
work with.
3. Adapter → Adaptee:
The Adapter calls the Adaptee’s methods, converting the client’s request into a form the
Adaptee understands. For example, it converts an ArrayList<String> into a String.
4. Adaptee:
The Adaptee performs its specialized functionality (e.g., printing a string). It doesn’t know
about the client or the Target interface; it simply does its job.
Software Examples of Adapter Patterns:
Wrappers used to adopt 3rd parties’ libraries and frameworks - most of the applications
using third party libraries use adapters as a middle layer between the application and the 3rd party
library to decouple the application from the library. If another library has to be used only an adapter
for the new library is required without having to change the application code.
Implementation
The Two-Ways Adapters are adapters that implements both interfaces of Target and Adaptee. The
adapted object can be used as Target in new systems dealing with Target classes or as Adaptee in
other systems dealing with Adaptee classes. Adapter can call the method of the existing class (since
the Adapter inherits from the existing class) and can work in the existing library (since
the Adapter implements the interface of the existing library).
4
Sample Code (Implementation)
1. PrintString.java (Adaptee):
This class represents the Adaptee in the Adapter Design Pattern. The PrintString class only knows
how to print a single String. It doesn’t directly deal with an ArrayList<String>, so we need an
adapter to handle this difference.
Description: The PrintString class has a print method that accepts a String and prints it. This class
is unaware of ArrayList<String>. It serves as the existing functionality that the client does not
directly work with.
2. PrintableList.java (Target Interface)
This is the target interface that the client interacts with. The client deals with an arrayList<String>,
so the PrintableList interface defines a contract for printing a list.
Description: The PrintableList interface defines a method printList, which takes an
ArrayList<String> as input. This is the target interface that the client uses to interact with the
underlying system (Adaptee).
5
3. PrintableListAdapter.java (Adapter)
This class acts as the Adapter between the PrintableList interface (expected by the client) and the
PrintString class (which deals with individual String objects).
Description: PrintableListAdapter implements the PrintableList interface, making it compatible
with the client. It converts the ArrayList<String> into a single String that can be passed to the
PrintString class. This conversion bridges the gap between the client and the adaptee.
4. AdapterDesignPatternMain.java (Client)
This is the client class where the adapter is utilized to print a list of strings. The client doesn’t
directly interact with PrintString; instead, it uses the PrintableList interface through the adapter.
6
Description: The AdapterDesignPatternMain class demonstrates how the client works with the
PrintableList interface. The client prepares an ArrayList<String>, then delegates the printing task
to PrintableListAdapter. The adapter internally uses PrintString to print the data.
Lab Tasks:
1. Implement an adapter class that can bridge the communication gap between two
incompatible classes. The adapter should wrap one of the classes and provide a compatible
interface that the other class can use to communicate with it.
2. Create an adapter class that implements the new system's interface and wraps the existing
class, modifying its methods as necessary to meet the new system's requirements. The
adapter class should provide a compatible interface that the new system can use to
communicate with the existing class.