Interface Composition
In this page:
Embedding Interfaces
An interface can embed another interface simply by naming it inside its own body, which pulls in all of that interface's method requirements without repeating their signatures.
Example: Embedding Interfaces
package main
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(string)
}
type ReadWriter interface {
Reader
Writer
}
type Buffer struct {
data string
}
func (b *Buffer) Read() string { return b.data }
func (b *Buffer) Write(s string) { b.data += s }
func main() {
var rw ReadWriter = &Buffer{}
rw.Write("hello")
fmt.Println(rw.Read())
}
Login to try C/C++/Java/PHP code in the editor
The Standard Library's io.ReadWriter
The standard library follows this exact pattern with io.ReadWriter, which embeds both io.Reader and io.Writer -- any type implementing both Read and Write methods automatically satisfies io.ReadWriter with no extra code.
Note: This composition pattern is exactly how the real io.ReadWriter type is defined.
Example: The Standard Library's io.ReadWriter
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer // *bytes.Buffer satisfies io.ReadWriter
buf.Write([]byte("composed interfaces"))
fmt.Println(buf.String())
}
Login to try C/C++/Java/PHP code in the editor
Satisfying a Composed Interface
To satisfy a composed interface, a type simply needs to implement every method required by each embedded interface -- there's nothing special about composition from the implementing type's point of view; it's just a larger method set to fulfill.
Example: Satisfying a Composed Interface
package main
import "fmt"
type Named interface {
Name() string
}
type Aged interface {
Age() int
}
type Person interface {
Named
Aged
}
type Employee struct {
name string
age int
}
func (e Employee) Name() string { return e.name }
func (e Employee) Age() int { return e.age }
func main() {
var p Person = Employee{name: "Nikhil", age: 29}
fmt.Println(p.Name(), p.Age())
}
Login to try C/C++/Java/PHP code in the editor
- Manually re-listing every method from another interface instead of simply embedding that interface by name.
- Assuming a composed interface changes how satisfaction works -- a type must still implement every method from all embedded interfaces, nothing is optional.
- Creating deeply nested composed interfaces that become hard to trace back to their required method set.
- Interfaces can embed other interfaces, combining their method sets into one larger interface.
- io.ReadWriter is a standard-library example: it embeds both io.Reader and io.Writer.
- A type must implement every method from all embedded interfaces to satisfy the composed interface.
- Composition lets you build larger contracts from small, well-understood pieces instead of duplicating method lists.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: