Override a struct method
NOTE There is no object-oriented Java-like overriding in Go. In this post I explain what I call overriding struct methods in Go.
There are times when we want to make a small modification to a complex struct. Instead of changing the original struct, we could create a new struct and embed the existing implementation inside it.
Let’s look at an example. Let’s say we are using the Person struct in a package defined like this:
package person type Person struct{} func (Person) Greet() string { return "hello" } func (Person) Bye() string { return "bye" }
This is how we use this package:
func main() { p := person.Person{} fmt.Println(p.Greet(), p.Bye()) }
hello bye
Okay. What if we need the Greet func to return something else? We don’t want to modify the struct directly because it is in another package we don’t maintain. And we don’t want to have to rewrite all the other methods.
To do that, we create a new struct like this:
// FrenchPerson embeds person.Person type FrenchPerson struct{ person.Person } // Greet returns "bonjour" func (FrenchPerson) Greet() string { return "bonjour" }
Finally, let’s test this new method:
func main() { p1 := Person{} fmt.Println(p1.Greet(), p1.Bye()) p2 := FrenchPerson{} fmt.Println(p2.Greet(), p2.Bye()) }
hello bye
bonjour bye
We are calling Greet and Bye as before. However, this time the overriden Greet func is called.
Real world example
I find this particularly useful for overriding methods in an external dependency.
Take for example the blackfriday markdown rendering package. It defines a Renderer interface with 31 functions:
type Renderer interface { // block-level callbacks BlockCode(out *bytes.Buffer, text []byte, lang string) BlockQuote(out *bytes.Buffer, text []byte) BlockHtml(out *bytes.Buffer, text []byte) Header(out *bytes.Buffer, text func() bool, level int, id string) HRule(out *bytes.Buffer) List(out *bytes.Buffer, text func() bool, flags int) ListItem(out *bytes.Buffer, text []byte, flags int) Paragraph(out *bytes.Buffer, text func() bool) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) TableRow(out *bytes.Buffer, text []byte) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) TableCell(out *bytes.Buffer, text []byte, flags int) Footnotes(out *bytes.Buffer, text func() bool) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) TitleBlock(out *bytes.Buffer, text []byte) // Span-level callbacks AutoLink(out *bytes.Buffer, link []byte, kind int) CodeSpan(out *bytes.Buffer, text []byte) DoubleEmphasis(out *bytes.Buffer, text []byte) Emphasis(out *bytes.Buffer, text []byte) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) LineBreak(out *bytes.Buffer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) RawHtmlTag(out *bytes.Buffer, tag []byte) TripleEmphasis(out *bytes.Buffer, text []byte) StrikeThrough(out *bytes.Buffer, text []byte) FootnoteRef(out *bytes.Buffer, ref []byte, id int) // Low-level callbacks Entity(out *bytes.Buffer, entity []byte) NormalText(out *bytes.Buffer, text []byte) // Header and footer DocumentHeader(out *bytes.Buffer) DocumentFooter(out *bytes.Buffer) GetFlags() int }
For this blog, all I want to do is to override the BlockCode func and keep everything else unchanged. I used the same technique as the above example. This is what I did:
type Renderer struct {
*blackfriday.Html
}
func (options *Renderer) BlockCode(out *bytes.Buffer,
text []byte, lang string) {
// ... new implementation ...
}
The code for my blog engine is here.