Decouple
Always start with data, which is your concrete. Never start with de-coupling, i.e, behaviour. If you start with behaviour, you are guessing data which is the wrong way to do it.
Methods
Go has methods, these are functions that have receivers. Treat methods as exceptions, functions should be the first thing to go for.
Methods can use value semantics or pointer semantics. Always stick to either one and never mix them.
When to use value semantics and pointer semantics
Semantic consistency is everything
We deal with 3 data types in go:
- Built in types (int, etc)
- User defined types (structs)
- Reference types (slices, maps, interfaces, etc)
For built in types, use value semantics
.
For reference types, use value semantics
. Exception: unless you take a pointer of a slice to a decode or Unmarshal function down the call stack.
For structs, choose one semantic, depending on the struct.
Example: If you have a struct representing time
. If you mutate the time by adding 5 seconds, its a new time variable. For this situation value
semantics make sense whereas for a struct user
, modifying the user email, is still the same user, this case pointer semantics make sense.
The factory
function written by the developer defines the semantic at play.
The wrong way to think about this
- Use value semantics for read
- Use pointer semantic if we want to mutate
The above is a wrong way to choose a semantic. We can very well use value semantics for mutation, a function that follows value semantics will mutate its own copy and return a new value. This is a preferred way to do things.
We do not make copies of shared data. This is against the semantics play.
avoid setters and getters. They add no value and just add more code and testing.
Method representation under the hood A method uses 2 words. 1 word for the function code and the 2nd word holds the data. If the method uses value semantics, the 2nd word points to the copied data. If the method uses pointer semantics, the 2nd word points to the original data.
Interfaces
Interfaces describe behaviour. They are not types, not concrete data types.
Embedding
Inner type promotion. Inner types can be directly accessed from the outer types.
Exporting
If type starts with capital letter, it is exported outside its package.