Java Adapter Design Pattern
Adapter Design Pattern
The Adapter Design Pattern is classified as a Structural design pattern.
Its main role is to act as a bridge between two incompatible interfaces, allowing them to work together.
It achieves this by creating a new interface—the Adapter—which enables an existing class to be used with other classes without changing its source code.
In essence, the Adapter pattern converts the interface of one class into an interface expected by the Client.
Why Should Java Programmers Study the Adapter Design Pattern?
-
Integration with Legacy Code:
Java has a long history and many legacy systems. The Adapter pattern allows older components to work with newer implementations without a complete rewrite. -
Facilitating Third-Party Libraries:
Java’s ecosystem includes countless third-party libraries whose interfaces may not align with your system. Adapters bridge this gap cleanly. -
Enhancing Code Reusability:
Existing components with useful behavior can be reused by adapting their interfaces instead of rebuilding functionality from scratch. -
Promotion of Modularity:
By separating interface adaptation from core logic, the Adapter pattern improves maintainability and clarity. -
Incremental System Evolution:
New behaviors can be introduced gradually without destabilizing existing workflows. -
Open/Closed Principle Adherence:
The Adapter pattern aligns with SOLID principles by extending behavior without modifying existing code. -
Simplification of Interfaces:
Adapters can present a simpler, more convenient interface when the original one is too complex. -
Multiple Interface Adaptations:
Multiple adapters can be built for the same class to support different systems or APIs.
Given Java’s prominence in enterprise and open-source environments, understanding the Adapter pattern is essential for building adaptable, maintainable systems that evolve gracefully.
Structural Pattern — Adapter (for Java Students)
Target
- Defines the interface the Client expects to use
- Represents standard system behavior
- Used directly by the Client
Client
- Works with objects through the Target interface
- Does not know about the Adaptee’s interface
- Uses Adapter to communicate correctly
Adaptee
- Has an existing interface incompatible with Target
- Contains useful behavior
- Cannot be easily modified
Adapter
- Implements the Target interface
- Translates Client requests into Adaptee calls
- Safely connects incompatible interfaces
Project Layout
/adapter-gof-demo Target.java Client.java Adaptee.java Adapter.java Main.java
Target.java
public interface Target {
String Request();
}
Adaptee.java
public class Adaptee {
private String adapteeValue;
public Adaptee(String adapteeValue) {
this.adapteeValue = adapteeValue;
}
public String SpecificRequest() {
return "Adaptee.SpecificRequest(): adapteeValue=" + adapteeValue;
}
public String getAdapteeValue() {
return adapteeValue;
}
public void setAdapteeValue(String adapteeValue) {
this.adapteeValue = adapteeValue;
}
}
Adapter.java
public class Adapter implements Target {
private Adaptee adaptee;
private String prefix;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
this.prefix = "[Adapter default prefix] ";
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
@Override
public String Request() {
return prefix + "Translated -> " + adaptee.SpecificRequest();
}
}
Client.java
public class Client {
private Target target;
public Client(Target target) {
this.target = target;
}
public void Operation() {
System.out.println(target.Request());
}
}
Main.java (Demo)
public class Main {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee("ORIGINAL_ADAPTEE_VALUE");
Adapter adapter = new Adapter(adaptee);
Client client = new Client(adapter);
System.out.println("=== First call ===");
client.Operation();
System.out.println("Adaptee value: " + adaptee.getAdapteeValue());
adapter.setPrefix("[Adapter UPDATED prefix] ");
System.out.println("\n=== Second call ===");
client.Operation();
System.out.println("Adaptee value (unchanged): " + adaptee.getAdapteeValue());
adaptee.setAdapteeValue("ADAPTEE_VALUE_CHANGED_DIRECTLY");
System.out.println("\n=== Third call ===");
client.Operation();
System.out.println("Adaptee value (changed): " + adaptee.getAdapteeValue());
}
}
What This Demo Proves
- The Client depends only on the Target interface
- The Adapter translates calls without modifying Adaptee
- Changing Adapter state does not mutate Adaptee state
- Only direct calls modify Adaptee’s internal value
S.W.O.T. Analysis of the Adapter Design Pattern in Java
Strengths
- Enables interface compatibility
- Encourages reuse of existing implementations
- Improves architectural flexibility
Weaknesses
- Adds indirection and possible overhead
- Can be confusing for beginners
- Becomes obsolete if legacy code is replaced
Opportunities
- Legacy system modernization
- Third-party API adaptation
- Cross-platform Java support
Threats
- Overuse can clutter architecture
- Refactoring may be a better solution
- Performance impact in real-time systems

No comments:
Post a Comment