Skip to content

Latest commit

 

History

History
105 lines (80 loc) · 2.13 KB

composition.md

File metadata and controls

105 lines (80 loc) · 2.13 KB

Object-Oriented Programming

Composition

We've now introduced the building blocks for Go's powerful composition techniques: custom types, methods, interfaces, and struct embedding.

"Everyone knows composition is more powerful than inheritance, Go just makes this non optional." — Dave Cheney: http://bit.ly/dctlg

Composition is more than just type embedding; it's about declaring and implementing discrete behaviour, creating types that have a single purpose, and then using these blocks to build higher level behaviours by composing the discrete behaviours.

Let's put all these techniques together. First we'll define a type representing a location, and two interfaces detailing behaviour.

type point struct {
	x, y int
}

type mover interface {
	moveTo(p point)
}

type firer interface {
	fire()
}

Now we'll define a new type for a vehicle, embedding the location. We'll also make this type satisfy one of our interfaces.

type vehicle struct {
	point
	passengers int
}

func (v *vehicle) moveTo(p point) {
	v.point = p
}

Let's also have a weapon, again satisfying the appropriate interface.

type weapon struct {
	loaded bool
}

func (w *weapon) fire() {
	w.loaded = false
}

With a bit more struct embedding, we can compose our vehicle and weapon types to create a tank.

type tank struct {
	vehicle
	weapon
}

Here we'll introduce a familiar concept with a slight twist: it's type embedding, but with an interface. It works in exactly the same way as with a struct.

type moverFirer interface {
	mover
	firer
}

func moveAndFire(mf moverFirer, p point) {
	mf.moveTo(p)
	mf.fire()
}

And finally, let's wrap everything together. We'll create a new tank, and then use our moveAndFire function to do just that.

func main() {
	t := &tank{
		vehicle{point{5, 6}, 6},
		weapon{true},
	}

	moveAndFire(t, point{10, 20})

	fmt.Printf("Location: %v; Passengers: %d; Loaded: %t\n",
		t.point, t.passengers, t.loaded)

	// Location: {10 20}; Passengers: 6; Loaded: false
}

A complete, runnable version of the above example can be found on the Go Playground.