π€ What is the Adapter Pattern?
The Adapter Pattern is a structural design pattern that allows classes with incompatible interfaces to work together. Just like a power adapter you use when traveling, it serves as a bridge connecting systems with different specifications.
π― Key Features
- Integration with new interfaces without modifying existing code
- Bridge between legacy systems and new systems
- Clean design following the Dependency Inversion Principle
π Java Example: Ocean Creature Adapter
An example where various marine creatures move in different ways but are managed through a common interface.
// π Existing Fish Interface
interface Fish {
void swim();
}
// π¦ Shark Class
class Shark implements Fish {
@Override
public void swim() {
System.out.println("π¦ Shark swims fast!");
}
}
// π Octopus Class (Different Interface)
class Octopus {
public void crawl() {
System.out.println("π Octopus crawls on the ocean floor!");
}
}
// π§ Octopus Adapter
class OctopusAdapter implements Fish {
private Octopus octopus;
public OctopusAdapter(Octopus octopus) {
this.octopus = octopus;
}
@Override
public void swim() {
// Convert octopus crawl to swim
octopus.crawl();
System.out.println("π« Octopus moves like swimming!");
}
}
// π Ocean Ecosystem Manager
class OceanManager {
private List<Fish> marineLife = new ArrayList<>();
public void addFish(Fish fish) {
marineLife.add(fish);
}
public void makeAllSwim() {
System.out.println("π Ocean creatures are moving:");
marineLife.forEach(Fish::swim);
}
}
// Usage Example
public class AdapterPatternExample {
public static void main(String[] args) {
OceanManager ocean = new OceanManager();
// Add regular fish
ocean.addFish(new Shark());
// Add octopus wrapped with adapter
Octopus octopus = new Octopus();
ocean.addFish(new OctopusAdapter(octopus));
ocean.makeAllSwim();
}
}
βοΈ TypeScript Example: Sky Wing Adapter
An example integrating different flying methods of various creatures in the sky.
// βοΈ Existing Flight Interface
interface Flyer {
fly(): void;
}
// π¦
Eagle Class
class Eagle implements Flyer {
fly(): void {
console.log("π¦
Eagle spreads its wings and flies!");
}
}
// π Helicopter Class (Different Interface)
class Helicopter {
rotate(): void {
console.log("π Helicopter rotates its rotor!");
}
}
// π§ Helicopter Adapter
class HelicopterAdapter implements Flyer {
private helicopter: Helicopter;
constructor(helicopter: Helicopter) {
this.helicopter = helicopter;
}
fly(): void {
// Convert helicopter rotate to fly
this.helicopter.rotate();
console.log("π« Helicopter flies through the sky!");
}
}
// π Balloon Class (Another Different Interface)
class Balloon {
float(): void {
console.log("π Balloon floats up in the air!");
}
}
// π§ Balloon Adapter
class BalloonAdapter implements Flyer {
private balloon: Balloon;
constructor(balloon: Balloon) {
this.balloon = balloon;
}
fly(): void {
// Convert balloon float to fly
this.balloon.float();
console.log("π« Balloon flies through the sky!");
}
}
// βοΈ Sky Traffic Controller
class SkyController {
private flyers: Flyer[] = [];
addFlyer(flyer: Flyer): void {
this.flyers.push(flyer);
}
makeAllFly(): void {
console.log("βοΈ Everything in the sky is flying:");
this.flyers.forEach(flyer => flyer.fly());
}
}
// Usage Example
const skyController = new SkyController();
// Add regular flyer
skyController.addFlyer(new Eagle());
// Add helicopter wrapped with adapter
const helicopter = new Helicopter();
skyController.addFlyer(new HelicopterAdapter(helicopter));
// Add balloon wrapped with adapter
const balloon = new Balloon();
skyController.addFlyer(new BalloonAdapter(balloon));
skyController.makeAllFly();
π Python Example: Ground Movement Adapter
An example integrating movement methods of various creatures and vehicles on land.
from abc import ABC, abstractmethod
# πΆ Existing Movement Interface
class Walker(ABC):
@abstractmethod
def walk(self):
pass
# π¦ Zebra Class
class Zebra(Walker):
def walk(self):
print("π¦ Zebra walks on four legs!")
# π Snake Class (Different Interface)
class Snake:
def slither(self):
print("π Snake slithers by wriggling its body!")
# π§ Snake Adapter
class SnakeAdapter(Walker):
def __init__(self, snake: Snake):
self.snake = snake
def walk(self):
# Convert snake slither to walk
self.snake.slither()
print("π« Snake moves like walking!")
# π Car Class (Another Different Interface)
class Car:
def drive(self):
print("π Car runs by rolling its wheels!")
# π§ Car Adapter
class CarAdapter(Walker):
def __init__(self, car: Car):
self.car = car
def walk(self):
# Convert car drive to walk
self.car.drive()
print("π« Car moves like walking!")
# π€ Robot Class (Another Different Interface)
class Robot:
def move_mechanically(self):
print("π€ Robot moves mechanically!")
# π§ Robot Adapter
class RobotAdapter(Walker):
def __init__(self, robot: Robot):
self.robot = robot
def walk(self):
# Convert robot move_mechanically to walk
self.robot.move_mechanically()
print("π« Robot moves like walking!")
# π Ground Manager
class GroundManager:
def __init__(self):
self.walkers = []
def add_walker(self, walker: Walker):
self.walkers.append(walker)
def make_all_walk(self):
print("π Everything on the ground is moving:")
for walker in self.walkers:
walker.walk()
# Usage Example
if __name__ == "__main__":
ground_manager = GroundManager()
# Add regular walker
ground_manager.add_walker(Zebra())
# Add snake wrapped with adapter
snake = Snake()
ground_manager.add_walker(SnakeAdapter(snake))
# Add car wrapped with adapter
car = Car()
ground_manager.add_walker(CarAdapter(car))
# Add robot wrapped with adapter
robot = Robot()
ground_manager.add_walker(RobotAdapter(robot))
ground_manager.make_all_walk()
π‘ Advantages of the Adapter Pattern
β Benefits
- Integrate new functionality without modifying existing code
- Follow the Single Responsibility Principle
- Maintain compatibility with legacy systems
- Increase code reusability
β οΈ Considerations
- May increase code complexity
- Performance degradation with too many adapters
- Need to modify adapters when interfaces change
π― When to Use?
- When integrating legacy systems with new systems
- When external library interfaces don't match
- When converting between different data formats
- When integrating communication interfaces between microservices
π Conclusion
The Adapter Pattern is a very practical pattern in software development. Just like adapters in real life, it allows different systems to connect and create greater value.
If you're struggling with incompatible interfaces, try applying the Adapter Pattern! π
λκΈ