The Adapter pattern allows objects with incompatible interfaces to collaborate by converting the interface of one class into another interface that clients expect.
The Adapter pattern is a structural design pattern that acts as a bridge between two incompatible interfaces. This implementation demonstrates a language translation scenario where an English speaker needs to communicate with a Japanese speaker through an adapter.
[Image: Adapter Pattern Class Diagram]
<!-- Add a class diagram showing Target, Adapter, Adaptee, and Client relationships -->
-
Target Interfaces
EnglishSpeakingEntity Interface (
EnglishSpeakingEntity.java)public interface EnglishSpeakingEntity { void speakEnglish(String text); }
JapaneeseSpeakingEntity Interface (
JapaneeseSpeakingEntity.java)public interface JapaneeseSpeakingEntity { void speakJapaneese(String text); }
-
Concrete Implementations
EnglishGuy Class (
EnglishGuy.java)- Role: Adaptee
- Purpose: Represents an English speaker with incompatible interface
- Key Features:
- Implements EnglishSpeakingEntity interface
- Has a name for identification
- Speaks only in English
public class EnglishGuy implements EnglishSpeakingEntity { private String name; public EnglishGuy(String name) { this.name = name; } public void speakEnglish(String text) { System.out.println(name + " speaking: " + text); } }
JapanGuy Class (
JapanGuy.java)- Role: Target
- Purpose: Represents a Japanese speaker
- Key Features:
- Implements JapaneeseSpeakingEntity interface
- Has a name for identification
- Speaks only in Japanese
public class JapanGuy implements JapaneeseSpeakingEntity { private String name; public JapanGuy(String name) { this.name = name; } public void speakJapaneese(String text) { System.out.println(this.name + " speaking: " + text); } }
-
AdapterEnglishToJapan Class (
AdapterEnglishToJapan.java)- Role: Adapter
- Purpose: Translates English speech to Japanese speech
- Key Features:
- Implements JapaneeseSpeakingEntity interface (target)
- Holds reference to EnglishSpeakingEntity (adaptee)
- Provides translation functionality
- Bridges the gap between incompatible interfaces
public class AdapterEnglishToJapan implements JapaneeseSpeakingEntity { private EnglishSpeakingEntity enEntity; public AdapterEnglishToJapan(EnglishSpeakingEntity enEntity) { this.enEntity = enEntity; } @Override public void speakJapaneese(String text) { System.out.println("Translating english text to japaneese:"); enEntity.speakEnglish(text); translateEngToJpn(text); } private void translateEngToJpn(String enText) { System.out.println("あなたは私を雇うべきです (mocked Japan text - translation for simplicity of DP was not implemented)"); } }
// Create an English speaker (Adaptee)
EnglishGuy englishGuy = new EnglishGuy("John");
// Create a Japanese speaker (Target)
JapanGuy japanGuy = new JapanGuy("Tanaka");
// Create adapter to make English speaker compatible with Japanese interface
AdapterEnglishToJapan adapter = new AdapterEnglishToJapan(englishGuy);
// Now we can use the English speaker through the Japanese interface
adapter.speakJapaneese("Hello, how are you?");
// Output:
// Translating english text to japaneese:
// John speaking: Hello, how are you?
// あなたは私を雇うべきです (mocked Japan text - translation for simplicity of DP was not implemented)
// Direct usage of Japanese speaker
japanGuy.speakJapaneese("こんにちは");
// Output: Tanaka speaking: こんにちはThis Adapter pattern implementation is ideal for:
- Legacy System Integration: Integrating old systems with new interfaces
- Third-party Library Integration: Adapting external libraries to your interface
- Data Format Conversion: Converting between different data formats
- API Compatibility: Making incompatible APIs work together
- Protocol Translation: Converting between different communication protocols
- Interface Compatibility: Makes incompatible interfaces work together
- Code Reusability: Reuse existing code without modification
- Separation of Concerns: Separates interface conversion from business logic
- Legacy Support: Enables integration with legacy systems
- Flexibility: Easy to add new adapters for different interfaces
- Code Complexity: Adds additional layer of abstraction
- Performance Overhead: Extra method calls through adapter
- Maintenance: More classes to maintain
- Debugging Complexity: Can make debugging more difficult
-
Database Integration
- Adapting different database drivers to common interface
- Converting between different query formats
- Bridging ORM and legacy database systems
-
File Format Conversion
- Converting between different document formats (PDF, DOC, TXT)
- Image format conversion (JPEG, PNG, GIF)
- Audio/video format adaptation
-
Payment Processing
- Adapting different payment gateways (PayPal, Stripe, Square)
- Converting between different payment protocols
- Unifying payment interfaces
-
Communication Systems
- Protocol translation (HTTP to SOAP, REST to GraphQL)
- Message format conversion
- Language translation services
The TestAdapter.java file demonstrates:
- Creating English and Japanese speakers
- Using the adapter to enable communication
- Showing interface compatibility
- Demonstrating translation functionality
- Interface Segregation: Separate interfaces for different languages
- Composition over Inheritance: Adapter uses composition to hold adaptee
- Mock Translation: Simplified translation for pattern demonstration
- Clear Naming: Descriptive class and method names
- Single Responsibility: Each class has a single, clear purpose
-
Object Adapter (Current Implementation)
- Uses composition to hold adaptee
- More flexible, can adapt multiple adaptees
-
Class Adapter
- Uses multiple inheritance (not possible in Java)
- Available in languages supporting multiple inheritance
-
Two-Way Adapter
- Can adapt in both directions
- Implements both target and adaptee interfaces
- Interface Design: Keep adapter interfaces simple and focused
- Error Handling: Handle conversion errors gracefully
- Performance: Consider caching for expensive conversions
- Documentation: Clearly document what the adapter does
- Testing: Test both successful and error scenarios
- Over-Adaptation: Creating adapters when direct modification would be better
- Complex Adapters: Making adapters too complex with business logic
- Performance Issues: Not considering performance impact of adaptation
- Error Propagation: Not properly handling errors during adaptation
- Interface Pollution: Adding too many methods to adapter interfaces
- Bridge: Similar structure but different intent (abstraction vs. compatibility)
- Decorator: Adds behavior, while Adapter changes interface
- Facade: Simplifies interface, while Adapter makes interfaces compatible
- Proxy: Controls access, while Adapter changes interface
- Wrapper: General term that includes Adapter, Decorator, and Facade
| Pattern | Purpose | When to Use |
|---|---|---|
| Adapter | Interface compatibility | Incompatible interfaces need to work together |
| Bridge | Separate abstraction from implementation | Want to vary abstraction and implementation independently |
| Decorator | Add behavior dynamically | Want to add responsibilities without subclassing |
| Facade | Simplify complex interface | Want to provide simple interface to complex subsystem |
- Bidirectional Adapter: Support translation in both directions
- Multiple Language Support: Extend to support more languages
- Real Translation: Integrate with actual translation services
- Caching: Add caching for frequently translated phrases
- Error Handling: Improve error handling for translation failures
- Configuration: Make translation configurable
- Async Translation: Support asynchronous translation
- Translation Quality: Add translation quality metrics
- Generic Adapter: Create generic adapter framework
- Plugin Architecture: Support pluggable translation modules
- Chain of Adapters: Support chaining multiple adapters
- Adaptive Behavior: Adapter behavior changes based on context
- Metrics and Monitoring: Add performance and usage metrics