Adapter Design Pattern in Go

1. Definition

The Adapter Design Pattern allows incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces by wrapping the capabilities of one interface with an interface that another class expects.

2. Problem Statement

Imagine you have an old system with classes that your code relies on. Now, you need to integrate a new system that does similar things but has a different interface. Rewriting the old system to match the new system's interface would be costly and risky.

3. Solution

Use an adapter to bridge the gap between the old and new systems. The adapter wraps the new system's interface and transforms method calls from the old system's interface to method calls compatible with the new system's interface.

4. Real-World Use Cases

1. Connecting new hardware to older computer systems.

2. Integrating modern software systems with legacy systems.

3. Translating data from one format to another (e.g., XML to JSON).

5. Implementation Steps

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

2. Define a new interface that the client code expects.

3. Create an adapter class that implements the new interface and composes an instance of the old interface.

4. Implement the methods of the new interface in the adapter class by delegating to the old interface.

6. Implementation in Go

package main
import (
	"fmt"
)
// Step 1: Old System's interface
type OldPrinter interface {
	OldPrint() string
}
// Concrete implementation of OldPrinter
type MyOldPrinter struct{}
func (m *MyOldPrinter) OldPrint() string {
	return "print from old system"
}
// Step 2: New Interface
type NewPrinter interface {
	Print() string
}
// Step 3: Adapter
type PrinterAdapter struct {
	OldP OldPrinter
}
func (pa *PrinterAdapter) Print() string {
	return pa.OldP.OldPrint()
}
// Client code
func main() {
	oldPrinter := &MyOldPrinter{}
	adapter := &PrinterAdapter{OldP: oldPrinter}
	fmt.Println(adapter.Print())
}

Output:

print from old system

Explanation:

1. We have an OldPrinter interface representing the old system and its concrete implementation MyOldPrinter.

2. A new interface NewPrinter is defined for the client to use.

3. The PrinterAdapter acts as an adapter that implements the NewPrinter interface and has a reference to the OldPrinter interface.

4. Calls to Print() of the NewPrinter interface are delegated to the OldPrint() method of the old system via the adapter.

7. When to use?

Use the Adapter Pattern when:

1. You want to use an existing class but its interface doesn't match what you need.

2. You want to create a reusable class that cooperates with unrelated or unforeseen classes.

3. You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one.

The Adapter pattern lets you work with incompatible interfaces by providing a middle-layer that converts one interface to another.


Comments