
I. Adapter - Structural Pattern
The Adapter is a Structural Design Pattern that allows objects with incompatible interfaces to work together. It acts as a wrapper — taking an existing class and adapting it to a target interface that the client expects, without changing either the client or the original class.
The Adapter pattern is particularly useful when you want to avoid violating the Open/Closed Principle: instead of modifying an existing implementation to satisfy a new requirement, you simply write a new adapter that bridges the gap. The original code stays untouched; you only add.
Think of it like a power plug adapter — your laptop charger has a fixed connector, but you can travel anywhere in the world by carrying the right adapter. The charger doesn't change. The socket doesn't change. Only the adapter in the middle does the job.
II. Real-world Example
Imagine you're building a backend service that needs to make HTTP GET requests. At first, your team uses Fetch as the HTTP module. Later, a new requirement comes in to support Axios as an alternative — perhaps for certain environments or configurations.
Without the Adapter pattern, you'd need to rewrite the client code or introduce conditionals everywhere. With it, you define a single Http interface and create a separate adapter for each implementation. The client never needs to know which one is running underneath.
This scenario introduces the following components:
- Http (interface): Defines the contract — a single
Get(url string) (interface{}, error)method. Any HTTP module that wants to work with the client must satisfy this interface.
- Fetch: An existing HTTP module with its own
Getmethod. It's already doing its job — we don't touch it.
- Axios: Another HTTP module with the same
Getmethod signature, but a different internal implementation.
- FetchAdapter: Wraps a
*Fetchinstance and implements theHttpinterface by delegating toFetch.Get().
- AxiosAdapter: Wraps a
*Axiosinstance and implements theHttpinterface by delegating toAxios.Get().
- Client: Accepts any
Httpinterface and callsGet()— completely unaware of whether it's talking to Fetch or Axios underneath.
The class diagram for the above scenario:

III. Implementation
- Definition of the
Httpinterface:
package adapter
type Http interface {
Get(url string) (interface{}, error)
}Fetch— an existing HTTP module:
package adapter
import "fmt"
type Fetch struct{}
func (m *Fetch) Get(url string) (interface{}, error) {
fmt.Printf("Http Get with Fetch: %s\n", url)
return nil, nil
}Axios— another HTTP module:
package adapter
import "fmt"
type Axios struct{}
func (m *Axios) Get(url string) (interface{}, error) {
fmt.Printf("Http Get with Axios: %s\n", url)
return nil, nil
}FetchAdapter— adaptsFetchto theHttpinterface:
package adapter
type FetchAdapter struct {
Instance *Fetch
}
func (m *FetchAdapter) Get(url string) (interface{}, error) {
return m.Instance.Get(url)
}AxiosAdapter— adaptsAxiosto theHttpinterface:
package adapter
type AxiosAdapter struct {
Instance *Axios
}
func (m *AxiosAdapter) Get(url string) (interface{}, error) {
return m.Instance.Get(url)
}Client— uses only theHttpinterface:
package adapter
type Client struct{}
func (c *Client) Get(http Http, url string) (interface{}, error) {
return http.Get(url)
}- Running the example:
fmt.Println("*** Example Adapter ***")
client := adapter.Client{}
url := "https://example.com/api"
fetchAdapter := &adapter.FetchAdapter{Instance: &adapter.Fetch{}}
client.Get(fetchAdapter, url)
axiosAdapter := &adapter.AxiosAdapter{Instance: &adapter.Axios{}}
client.Get(axiosAdapter, url)
fmt.Print("*** End of Adapter ***\n\n\n")With the output:
*** Example Adapter ***
Http Get with Fetch: https://example.com/api
Http Get with Axios: https://example.com/api
*** End of Adapter ***IV. Explain the example above
Let's trace what actually happens step by step.
1. Define the target interface
type Http interface {
Get(url string) (interface{}, error)
}This is the target interface — the contract that the Client depends on. It doesn't care about Fetch or Axios at all. It only knows Http.
2. The existing modules — untouched
Fetch and Axios both have a Get method with the same signature. But they are not implementing Http directly — they are standalone structs with no knowledge of the interface. We never modify them.
3. The adapters bridge the gap
fetchAdapter := &FetchAdapter{Instance: &Fetch{}}FetchAdapter holds a reference to a *Fetch instance and satisfies the Http interface by simply delegating:
func (m *FetchAdapter) Get(url string) (interface{}, error) {
return m.Instance.Get(url)
}Same pattern for AxiosAdapter. The adapter is the thin wrapper that makes incompatible things compatible.
4. The client — zero changes when switching implementations
client.Get(fetchAdapter, url)
client.Get(axiosAdapter, url)The Client.Get() method receives any value that satisfies Http. It calls http.Get(url) — and Go's interface dispatch routes to the correct underlying implementation automatically.
Log result:
Http Get with Fetch: https://example.com/api
Http Get with Axios: https://example.com/apiSummary
If tomorrow a third HTTP module appears — say HttpClient — you only need to write one new adapter struct. The Client, Fetch, and Axios remain completely unchanged. This is the Open/Closed Principle in action.
V. Conclusion
The Adapter pattern is one of the most practical patterns in day-to-day software development. Any time you have a stable client that must work with multiple interchangeable implementations — HTTP clients, loggers, storage backends, payment gateways — Adapter gives you a clean way to plug them in without coupling your business logic to any specific library.
The key insight is: don't modify, adapt. Keep your existing code stable, keep your interface clean, and let the adapter carry the translation cost.
That said, as with all patterns, don't reach for Adapter when a simple function or direct call is sufficient. The value only becomes clear when you genuinely need to support multiple implementations behind a shared contract.
Thank you for reading! 😊
VI. References
- Go Design Patterns (Mario Castro Contreras)
- Full source code: available here.
