Adapter Design Pattern in Swift

1. Definition

The Adapter pattern, often known as the Wrapper pattern, is a structural design pattern that allows objects with incompatible interfaces to collaborate. It acts as a bridge between two interfaces. The Adapter pattern involves creating a new adapter class that combines the interface of one class with the interface of another.

2. Problem Statement

Imagine a scenario where you have an old component in your software that works perfectly fine but has an outdated interface. Now, you're introducing a new system that expects objects to have a different interface. Directly integrating these systems would be problematic due to interface incompatibilities.

3. Solution

The Adapter pattern suggests creating a separate adapter class that translates calls from the new system to the old component without changing any existing code. The new system communicates with the adapter, and the adapter communicates with the old component.

4. Real-World Use Cases

1. Modern USB-C to old USB-A converters.

2. Adapting new UI components to work with legacy backend systems.

3. Integrating third-party libraries with different methods or properties.

5. Implementation Steps

1. Identify the existing target interface that needs to be adapted.

2. Create a new adapter class that implements the new expected interface.

3. The adapter class will contain a reference to the instance of the old component.

4. The adapter will translate method calls from the new interface to the old component's methods.

6. Implementation in Swift Programming

// Step 1: Old component with a different interface.
class OldSystem {
    func oldRequest() -> String {
        return "Data from old system."
    }
}
// Step 2: New expected interface.
protocol NewSystemProtocol {
    func request() -> String
}
// Step 3: Adapter class.
class Adapter: NewSystemProtocol {
    private let oldSystem: OldSystem
    init(oldSystem: OldSystem) {
        self.oldSystem = oldSystem
    }
    // Translate new request to old request.
    func request() -> String {
        return oldSystem.oldRequest()
    }
}
// Usage:
let old = OldSystem()
let adapter = Adapter(oldSystem: old)
print(adapter.request())

Output:

Data from old system.

Explanation:

1. We have an OldSystem class with a method oldRequest that fetches some data.

2. Our new components expect objects to have a request method based on the NewSystemProtocol protocol.

3. The Adapter class acts as a bridge. It implements the new expected interface and contains a reference to the old component.

4. When the request method of the adapter is called, it translates that call to the oldRequest method of the old system.

7. When to use?

The Adapter pattern is useful when:

1. You want to use an existing class with a different interface.

2. You want to create a reusable class that works with classes with non-compatible interfaces.

3. You want to integrate systems or libraries with different interfaces without changing the source code of either.


Comments