๐ŸŽ“ A1Learner
๐Ÿ”
0 of 34 lessons completed

Go Interface Composition

Interface composition builds a larger interface from smaller interfaces. The larger interface asks for the methods of every part.

Two small interfaces in one

Fetcher names a Fetch method. Storer names a Store method. FetchStorer lists both interfaces inside its braces.

package main

import "fmt"

type Fetcher interface {
	Fetch() string
}

type Storer interface {
	Store(text string)
}

type FetchStorer interface {
	Fetcher
	Storer
}

type Memory struct {
	text string
}

func (m *Memory) Store(text string) {
	m.text = text
}

func (m Memory) Fetch() string {
	return m.text
}

func main() {
	var store FetchStorer = &Memory{}
	store.Store("Go notes")
	fmt.Println(store.Fetch())
}

A composed interface with Stringer

Label names a Name method. fmt.Stringer names a String method. LabelStringer lists both interfaces.

package main

import "fmt"

type Label interface {
	Name() string
}

type LabelStringer interface {
	Label
	fmt.Stringer
}

type Product struct {
	Title string
}

func (p Product) Name() string {
	return p.Title
}

func (p Product) String() string {
	return "Product: " + p.Title
}

func main() {
	var item LabelStringer = Product{Title: "Notebook"}
	fmt.Println(item.Name())
	fmt.Println(item)
}

A composed interface as a parameter

Actor lists Speaker and Mover. The perform function takes an Actor, so it can call Speak and Move.

package main

import "fmt"

type Speaker interface {
	Speak() string
}

type Mover interface {
	Move() string
}

type Actor interface {
	Speaker
	Mover
}

type Robot struct{}

func (r Robot) Speak() string {
	return "I can speak"
}

func (r Robot) Move() string {
	return "I can move"
}

func perform(a Actor) {
	fmt.Println(a.Speak())
	fmt.Println(a.Move())
}

func main() {
	perform(Robot{})
}

Example

package main

import "fmt"

type Reader interface {
	Read() string
}

type Writer interface {
	Write(text string)
}

type ReadWriter interface {
	Reader
	Writer
}

type Buffer struct {
	text string
}

func (b *Buffer) Write(text string) {
	b.text += text
}

func (b Buffer) Read() string {
	return b.text
}

func main() {
	var rw ReadWriter = &Buffer{}
	rw.Write("Hello ")
	rw.Write("Go")
	fmt.Println(rw.Read())
}

Key points

  • A composed interface lists a smaller interface by its name alone.
  • A type satisfies a composed interface when it has every method from every part.
  • fmt.Stringer is an interface that names one method, String.
  • The String method returns the text that fmt prints for a value.
  • A composed interface can serve as the type of a function parameter.
  • A function with that parameter can call every method the parts name.

In this example, ReadWriter lists Reader and Writer. The code stores a new Buffer in a ReadWriter value. The first Write call adds Hello with a trailing space. The second Write call adds Go. Read returns the stored text, and the print line shows Hello Go.

Note

The parts stay usable on their own beside the composed interface.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Go Playground

Exercise

A type has the Fetch method but no Store method. Can it satisfy FetchStorer?