Easy patterns in Go: The Adapter Pattern

Introduction

The Adapter pattern is used to make one interface compatible with another. It allows objects with different, and therefore incompatible interface to work together.

If you for example want to bring an existing class into your system with an interface that doesn’t match any existing interface, and you can not modify this class, you write an adapter class to bridge the gap.

It looks like this:

The pattern consists of different parts:

  • The client has an object which implements the Subject interface.
  • The object in question is the Adapter.
  • The implementation of the operation() method in the Adapter wraps the concrete_operation in the RealSubject class.

That way we can talk to the RealSubject class using an already known interface. The Adapter class takes care of converting our method-calls for the RealSubject. Also, because this pattern is loosely coupled, changing the RealSubject should be relatively painless.

What is the difference between the Decorator and the Adapter pattern?

DecoratorAdapter
PurposeThe decorator pattern is used to add behaviour to objects dynamically without modifying the source.Make one interface compatible with another
UseThis pattern is used to when you want to add or extend behaviour to existing classes in a flexible and reusable way without creating a hierarchy of subclasses.Used to integrate an existing class with an interface which is not compatible with existing interfaces. Modifying this class is not needed if you wrap it in an Adapter.
StructureCreating a decorator pattern involves creating a set of decorator classes that implement the same interface as the component, thereby extending or modifying its behaviourCreate an Adapter class, implementing the desired interface. This class also holds an instance of the class you want to adapted, the adaptee. The adapter delegates calls to the adaptee converting or transforming the input or output of these calls.
ExampleImagine having a bakery, you add things like GlazingDecorator or a WhippedCreamDecorator wrapping around your Pie-class to produces pies of the desired kindA classic example would be the conversion of units of measurement. If you have a classes which expects lengths to be in feet and inches, yet your client needs meters, you can wrap the classes in ConverterAdapter and have the conversion done automatically
Difference between Decorator and Adapter

In short, Adapter is used to make incompatible things work together, while the Decorator adds and extends functionality.

Implementation in Go

We will start with usual preliminaries:

package main

import (
	"fmt"
	"strconv"
)

A short explanation what we will build:

  • We have an existing class which doubles numbers, which takes an int16 as a parameter
  • However our client produces numbers as strings.

This is a very trivial example on purpose, but it touches on the important points of this pattern.

We will start with the Subject interface, that is the interface the client sees:

type Subject interface {
	Request(number string) string
}

This interface is self-explanatory

Next we define the ConcreteSubject struct. This is an empty struct because in this example, the ConcreteSubject does not hold any state:

type ConcreteSubject struct {
}

func (a *ConcreteSubject) SpecificRequest(number int16) int16 {
	return number * 2
}

Here we implement the SpecificRequest() method, which takes and returns an int16. This is the implementation of the new class, and we need to adapter to use this interface.

The Adapter struct looks like this:

type Adapter struct {
	ConcreteSubject
}

func (adapter *Adapter) Request(number string) string {
	n, _ := strconv.Atoi(number)
	result := fmt.Sprintf("%d", adapter.SpecificRequest(int16(n)))
	return result
}

A few notes:

  1. With the use of Go embedding, we embed the ConcreteSubject.
  2. Next we implement the Subject interface. The conversion between the two interfaces is done here, and the method delegates the call to Request() to SpecificRequest

Time to test

Before we can test, we need an extra function:

func clientCode(subject Subject) {
	fmt.Println(subject.Request("7"))
}

This is the simulation of the client-block in our diagram.

The main function looks like this:

func main() {
	subject := ConcreteSubject{}
	adapter := Adapter{subject}
	clientCode(&adapter)
}

Line by line:

  1. We construct an empty ConcreteSubject struct
  2. We pass that to the constructor of the adapter.
  3. In clientCode() we call the Request() method on the subject. The Adapter will handle this, since it implements the Subject interface.

Conclusion

This pattern can be quite confusing at first, at least that is what I found. The main points of this pattern are:

  1. You wrap the class with the incompatible or new interface in the Adapter class.
  2. Delegate method-calls where necessary to the ‘adaptee’ class, and make sure you convert both the input and output parameters where needed.

Once I realized this, implementing this pattern was quite easy. I realize that implementing this pattern in existing systems and legacy code might be a bit more work.