Several months ago, I had a story requiring metaprogramming in golang. I wasn’t very familiar with reflections in Go, and the available docs and write-ups aren’t the best for me, since I’m a learn-by-example kind of person. Having come to Go (ha) as a Rubyist, the lack of generics left a little bit of a hole in my heart, but a stiff upper lip, I pressed on and learned about reflections. Since I had to write my own examples anyway, it made sense to clean this up and turn it into a blog.
[]byte
with reflect.Type
as parameters, but only if previously registered.Pet
uses the Adopt([]byte)
function for deserialization. We’ll need to initialize the incoming type and call Adopt
, passing in the data.reflect.Type
and a validator (Veterinarian.Examine
in the code example) for this type, with a Registrar
. The type is expected to implement the Pet
interface.Pet
interface:Pet is a domesticated animal companion for humans. It can be “adopted” via Adopt.
type Pet interface {
Adopt([]byte) error
IsVaccinated() bool
IsHealthy() bool
PetName() string
Type() string
Vaccinate()
}
Cat
implements Pet
:type Cat struct {
Name string `json:"name"`
Healthy bool
Vaccinated bool `json:vaccinated`
}
func (f *Cat) Adopt(data []byte) error { return json.Unmarshal(data, f) }
func (f *Cat) IsVaccinated() bool { return f.Vaccinated }
func (f *Cat) IsHealthy() bool { return f.Healthy }
func (f *Cat) Adopt(data []byte) error { return json.Unmarshal(data, f) }
func (f *Cat) PetName() string { return f.Name }
func (f *Cat) Sound() string { return "meow!" }
func (f *Cat) Type() string { return "Felis Catus" }
func (f *Cat) Vaccinate() { f.Vaccinated = true }
We can ensure Cat implements the Pet interface. If it doesn’t, this doesn’t compile:
var _ Pet = &Cat{}
type Veterinarian struct {
vtype reflect.Type
Examine func(Pet) bool
}
type Registrar struct {
RegisteredPets map[string]Veterinarian
}
The rest of this is in the linked Gist below. I have annotated in detail the test code and implementations. If you download and run the code, particularly if you step through with a debugger, you can examine the test assertions. You can also add your own assertions to explore the answers to your own questions.
In the first set of tests, we examine the basics of reflection by playing around with initializing a Cat struct just by knowing its type, and then calling both Pet
and Cat
functions on it. The test assertions demonstrate the explanations.
In the next set of tests, we apply this knowledge to our deserialization task.
The final set of tests are to test the implementation of Veterinarian
and Registrar
. These are left for you to make pass, if you like.
Interested in more software development tips & insights? Visit the development section on our blog!
I'm a Principal Developer for Carbon Five.
Interested in a Career at Carbon Five? Check out our job openings.