Clean Interfaces
Writing clean interfaces is hard. Frankly, any time you’re dealing with abstractions in code, the simple can become complex very quickly if you’re not careful. Let’s go over some rules of thumb for keeping interfaces clean.
1. Keep Interfaces small
Interfaces are meant to define the minimal behaviour necessary to accurately represent an idea/concept
2. Interfaces should have no knowledge of satisfying types
An interface should define what is necessary for other types to classify as a member of that interface. They shouldn’t be aware of any types that happen to satisfy the interface at design time.
For example, when building the definition of a car
type car interface {
Color() string
Speed() int
IsFiretruck() bool
}
-
isFireTruck()
is an antipattern as we are forcing all cars to declare whether they are firestrucks, and for this pattern to make sense, we need a whole list of possible subtypes like isSedan(), isTank() etc... -
Instead, should rely on the native functionality of type assertion to check if a car is a firestruck
-
or define a sub-interface:
type firetruck interface {
car
HoseLength() int
}
3. Interfaces are not classes
- Interfaces are not classes, they are slimmer.
- Interfaces don’t have constructors or deconstructors that require that data is created or destroyed.
- Interfaces aren’t hierarchical by nature, though there is syntactic sugar to create interfaces that happen to be supersets of other interfaces.
- Interfaces define function signatures, but not underlying behavior. Making an interface often won’t DRY up your code in regards to struct methods. For example, if five types satisfy the fmt.Stringer interface, they all need their own version of the
String()
function.