Structural pattern · Gang of Four · Beginner

Adapter

Convert the interface of a type into another interface clients expect, letting otherwise-incompatible types work together.

Also known as — Wrapper

Structural Beginner Complete

🔌 Analogy

You travel abroad with a laptop charger that has the wrong plug shape. You don’t rewire the wall or buy a new laptop — you slot in a travel adapter. It exposes the socket your charger expects on one side and plugs into the foreign socket on the other. Same idea, in code.

The problem

Your client code is written against a Duck (it calls Fly and Quack). You have a perfectly good Turkey, but it speaks a different interface — it Flys and Gobbles. You can’t change Turkey (pretend it’s a third-party package). An Adapter wraps the turkey and presents it as a duck.

Structure

classDiagram
  class Duck {
    <<interface>>
    +Fly()
    +Quack()
  }
  class Turkey {
    +Fly()
    +Gobble()
  }
  class TurkeyAdapter {
    -turkey Turkey
    +Fly()
    +Quack()
  }
  Duck <|.. TurkeyAdapter
  TurkeyAdapter o--> Turkey : wraps & translates

TurkeyAdapter satisfies Duck, and inside each method it translates the call to the turkey’s vocabulary — Quack() becomes Gobble().

Idiomatic Go

Because interfaces are implicit, the adapter just needs the right methods. Edit and Run:

package main

import "fmt"

// Target: the interface the client wants.
type Duck interface {
	Fly()
	Quack()
}

// Adaptee: an existing type with an incompatible interface.
type Turkey struct{}

func (Turkey) Fly()    { fmt.Println("flying a short distance") }
func (Turkey) Gobble() { fmt.Println("gobble gobble") }

// Adapter: makes a Turkey usable wherever a Duck is expected.
type TurkeyAdapter struct{ turkey Turkey }

func (a TurkeyAdapter) Quack() { a.turkey.Gobble() } // translate the name
func (a TurkeyAdapter) Fly() {
	for i := 0; i < 5; i++ { // turkeys fly short, so flap a few times
		a.turkey.Fly()
	}
}

func main() {
	var duck Duck = TurkeyAdapter{turkey: Turkey{}}
	duck.Quack()
	duck.Fly()
}

🐹 Adapter vs Decorator

They look alike — both wrap an object — but they answer different questions. Decorator keeps the same interface and adds behavior. Adapter changes the interface so an object fits where it otherwise couldn’t. If you’re translating names/shapes, it’s an adapter; if you’re enriching the same contract, it’s a decorator.

In the standard library

  • strings.NewReader, bytes.NewReader — adapt a string/[]byte to io.Reader.
  • http.HandlerFunc — adapts a function to the http.Handler interface.
  • sort.Reverse — adapts a sort.Interface into one that sorts the other way.

Pitfalls

⚠️ A swarm of adapters is a smell

One or two adapters at a boundary with third-party code is healthy. But if you find yourself adapting everything to everything, your abstractions probably don’t line up — that’s a cue to rethink the interfaces rather than keep translating between them.

When to use it — and when not

✅ Reach for it when

  • You want to use an existing or third-party type, but its interface doesn't match what your code expects.
  • You can't (or don't want to) change the source type — it's legacy, vendored, or generated.
  • You need a stable interface boundary between your code and someone else's.

⛔ Think twice when

  • You control the source type and can just make it satisfy the interface directly.
  • Lots of adapters pile up — it may signal mismatched abstractions that deserve a redesign.

Check your understanding

Score: 0 / 3

1. What does an Adapter change?

Adapter translates between interfaces. The adaptee's methods (Gobble, Fly) are exposed through the target interface the client wants (Quack, Fly).

2. Why are adapters often trivial in Go?

No `implements` keyword: a wrapper that has Fly() and Quack() simply *is* a Duck. Even a function can be adapted (http.HandlerFunc).

3. Which standard-library type is a function Adapter?

http.HandlerFunc adapts a plain func(w, r) into the http.Handler interface by giving it a ServeHTTP method that just calls the function.