# Reducing Type Hierarchies

- Published: October 9, 2016
- Author: William Kennedy
- URL: https://www.ardanlabs.com/blog/2016/10/reducing-type-hierarchies.html

### Introduction

I see a lot of developers coming to Go from object oriented programming languages such as C# and Java. Because these developers have been trained to use [type hierarchies](http://bit.ly/1IR9T9j), it makes sense for them to use this same pattern in Go. However, there are aspects of Go that don’t allow type hierarchies to provide the same level of functionality they do in other object oriented programming languages. Specifically, the concepts of base types and subtyping don’t exist in Go so type reuse requires a different way of thinking.

In this post I am going to show why type hierarchies are not always the best pattern to use in Go. I’ll explain why it is better to group concrete types together not by a common state but by a common behavior. I’ll show how to leverage interfaces to group and decouple concrete types, and lastly, I will provide some guidelines around declaring types.

### Part I

Let’s start with a program I see way too often from those trying to learn Go. This program uses a traditional type hierarchy pattern that would be commonly seen in an object oriented program.

https://play.golang.org/p/ZNWmyoj55W

**Listing 1:**
```
01 package main
02
03 import "fmt"
04
05 // Animal contains all the base fields for animals.
06 type Animal struct {
07	Name     string
08	IsMammal bool
09 }
10
11 // Speak provides generic behavior for all animals and
12 // how they speak.
13 func (a Animal) Speak() {
14	fmt.Println("UGH!",
15		"My name is", a.Name,
16		", it is", a.IsMammal,
17		"I am a mammal")
18 }
```

In listing 1 we see the beginning of our traditional object oriented program. On line 06 we have the declaration of the concrete type `Animal` and it has two fields, `Name` and `IsMammal`. Then on line 13 we have a method named `Speak` that allows an `Animal` to talk. Since an `Animal` is a base type for all animals, the implementation of the `Speak` method is generic and can’t represent any given animal very well beyond this base state.

**Listing 2:**
```
20 // Dog contains everything an Animal is but specific
21 // attributes that only a Dog has.
22 type Dog struct {
23	Animal
24	PackFactor int
25 }
26
27 // Speak knows how to speak like a dog.
28 func (d Dog) Speak() {
29	fmt.Println("Woof!",
30		"My name is", d.Name,
31		", it is", d.IsMammal,
32		"I am a mammal with a pack factor of", d.PackFactor)
33 }
```

Listing 2 declares a new concrete type named `Dog` which embeds a value of type `Animal` and has a unique field named `PackFactor`. We see the use of composition to reuse the fields and methods of the `Animal` type. In this case, composition is providing some of the same benefits of inheritance, with respect to type reuse. The `Dog` type also implements its own version of the `Speak` method, which allows the `Dog` to bark like a dog. This method is overriding the implementation of the `Animal` type’s `Speak` method.

**Listing 3:**
```
35 // Cat contains everything an Animal is but specific
36 // attributes that only a Cat has.
37 type Cat struct {
38	Animal
39	ClimbFactor int
40 }
41
42 // Speak knows how to speak like a cat.
43 func (c Cat) Speak() {
44	fmt.Println("Meow!",
45		"My name is", c.Name,
46		", it is", c.IsMammal,
47		"I am a mammal with a climb factor of", c.ClimbFactor)
48 }
```

Next we have a third concrete type named `Cat` in listing 3 that also embeds a value of type `Animal` and has a field named `ClimbFactor`. We see the use of composition again for the same reasons, and `Cat` has a method named `Speak` that allows the `Cat` to meow like a cat. Again, this method is overriding the implementation of the `Animal` type’s `Speak` method.

**Listing 4:**
```
50 func main() {
51
52	// Create a Dog by initializing its Animal parts
53	// and then its specific Dog attributes.
54	d := Dog{
55		Animal: Animal{
56			Name:     "Fido",
57			IsMammal: true,
58		},
59		PackFactor: 5,
60	}
61
62	// Create a Cat by initializing its Animal parts
63	// and then its specific Cat attributes.
64	c := Cat{
65		Animal: Animal{
66			Name:     "Milo",
67			IsMammal: true,
68		},
69		ClimbFactor: 4,
70	}
71
72	// Have the Dog and Cat speak.
73	d.Speak()
74	c.Speak()
75 }
```

In listing 4 we have the main function where we put everything together. On line 54, we create a value of type `Dog` using a struct literal and initialize the embedded `Animal` value and the `PackFactor` field. On line 64, we create a value of type `Cat` using a struct literal and initialize the embedded `Animal` value and the `ClimbFactor` field. Then, finally, we call the `Speak` method against the `Dog` and `Cat` values on lines 73 and 74.

This works in Go, and you can see how the use of embedding types provides familiar type hierarchy functionality. However there are some flaws with doing this in Go, and one is that Go does not support the idea of subtyping. This means you can’t use the `Animal` type as a base type like you can in other object oriented languages.

What is important to understand is that, in Go, the `Dog` and `Cat` types can’t be used as a value of type `Animal`. What we have is an embedded value of type `Animal` inside a value of type `Dog` and `Cat`. You can’t pass a `Dog` or `Cat` to any function that accepts values of type `Animal`. This also means that there is no way to group a set of `Cat` and `Dog` values together in the same list via the `Animal` type.

**Listing 5:**
```
// Attempt to use Animal as a base type.
animals := []Animal{
    Dog{},
    Cat{},
}

: cannot use Dog literal (type Dog) as type Animal in array or slice literal
: cannot use Cat literal (type Cat) as type Animal in array or slice literal
```

Listing 5 shows what happens in Go when you try to use the `Animal` type as a base type in a traditional object oriented way. The compiler is very clear that the `Dog` and `Cat` types can’t be used as type `Animal`.

The `Animal` type and the use of type hierarchies in this case is not providing us any real value. I would argue it is leading us down a path of code that is not readable, simple or adaptable.

### Part II

When coding in Go try to avoid these type hierarchies that promote the idea of common state and think about common behavior. We can group a set of `Dog` and `Cat` values if we think about the common behavior they exhibit. In this case there is a common behavior of `Speak`.

Let’s look at another implementation of this code that focuses on behavior.

https://play.golang.org/p/6aLyTOTIj_

**Listing 6:**
```
01 package main
02
03 import "fmt"
04
05 // Speaker provide a common behavior for all concrete types
06 // to follow if they want to be a part of this group. This
07 // is a contract for these concrete types to follow.
08 type Speaker interface {
09	Speak()
10 }
```

The new program starts in listing 6 and we have added a new type called `Speaker` on line 08. This is not a concrete type like the struct types we declared before. This is an interface type that declares a contract of behavior that will let us group and work with a set of different concrete types that implement the `Speak` method.

**Listing 7:**
```
12 // Dog contains everything a Dog needs.
13 type Dog struct {
14	Name       string
15	IsMammal   bool
16	PackFactor int
17 }
18
19 // Speak knows how to speak like a dog.
20 // This makes a Dog now part of a group of concrete
21 // types that know how to speak.
22 func (d Dog) Speak() {
23	fmt.Println("Woof!",
24		"My name is", d.Name,
25		", it is", d.IsMammal,
26		"I am a mammal with a pack factor of", d.PackFactor)
27 }
28
29 // Cat contains everything a Cat needs.
30 type Cat struct {
31	Name        string
32	IsMammal    bool
33	ClimbFactor int
34 }
35
36 // Speak knows how to speak like a cat.
37 // This makes a Cat now part of a group of concrete
38 // types that know how to speak.
39 func (c Cat) Speak() {
40	fmt.Println("Meow!",
41		"My name is", c.Name,
42		", it is", c.IsMammal,
43		"I am a mammal with a climb factor of", c.ClimbFactor)
44 }
```

In listing 7 we have the declaration of the concrete types `Dog` and `Cat` again. This code removes the `Animal` type and copies those common fields directly into `Dog` and `Cat`.

Why did we do that?

* The `Animal` type was providing an abstraction layer of reusable state.
* The program never needed to create or solely use a value of type `Animal`.
* The implementation of the `Speak` method for the `Animal` type was a generalization.
* The `Speak` method for the `Animal` type was never going to be called.

Here are some guidelines around declaring types:

* Declare types that represent something new or unique.
* Validate that a value of any type is created or used on its own.
* Embed types to reuse existing behaviors you need to satisfy.
* Question types that are an alias or abstraction for an existing type.
* Question types whose sole purpose is to share common state.

Let’s look at the main function now.

**Listing 8:**
```
46 func main() {
47
48	// Create a list of Animals that know how to speak.
49	speakers := []Speaker{
50
51		// Create a Dog by initializing its Animal parts
52		// and then its specific Dog attributes.
53		Dog{
54			Name:       "Fido",
55			IsMammal:   true,
56			PackFactor: 5,
57		},
58
59		// Create a Cat by initializing its Animal parts
60		// and then its specific Cat attributes.
61		Cat{
62			Name:        "Milo",
63			IsMammal:    true,
64			ClimbFactor: 4,
65		},
66	}
67
68	// Have the Animals speak.
69	for _, spkr := range speakers {
70		spkr.Speak()
71	}
72 }
```

On line 49 in listing 8, we create a slice of `Speaker` interface values to group both `Dog` and `Cat` values together under their common behavior. We create a value of type `Dog` on line 53 and a value of type `Cat` on line 61. Finally on lines 69 - 71 we iterate over the slice of `Speaker` interface values and have the `Dog` and `Cat` speak.

Some final thoughts about the changes we made:

* We didn’t need a base type or type hierarchies to group concrete type values together.
* The Interface allowed us to create a slice of different concrete type values and work with these values through their common behavior.
* We removed any type pollution by not declaring a type that was never solely used by the program.

#### Conclusion

There is a lot more to composition in Go but this is an initial understanding around the problems with using type hierarchies. There are always exceptions to every rule, but try to follow these guidelines until you know enough to understand the tradeoffs for making an exception.

To learn more about composition and other topics this post touches, check out these other blog posts:

[Exported/Unexported Identifiers In Go](https://www.ardanlabs.com/blog/2014/03/exportedunexported-identifiers-in-go.html)  
[Methods Interfaces And Embedded Types](https://www.ardanlabs.com/blog/2014/05/methods-interfaces-and-embedded-types.html)  
[Object Oriented Mechanics In Go](https://www.ardanlabs.com/blog/2015/03/object-oriented-programming-mechanics.html)  
[Composition With Go](https://www.ardanlabs.com/blog/2015/09/composition-with-go.html)

#### Thanks

Here are some friends from the community I would like to thank for taking the time to review the post and provide feedback.

[Daniel Vaughan](https://twitter.com/DanielVaughan), [Ted Young](https://twitter.com/jitterted), [Antonio Troina](/broken-link), [Adam Straughan](https://twitter.com/nearlyadam), [Kaveh Shahbazian](/broken-link), [Daniel Whitenack](https://twitter.com/dwhitena), [Todd Rafferty](https://twitter.com/webRat)
