I want to do inheritance, but I know that's wrong -- what's the right way?

419 views
Skip to first unread message

Richard Sugg

unread,
Nov 28, 2015, 1:46:08 PM11/28/15
to golang-nuts
In another application in another language, I have a package that works with bitmasks. The base class receives an assoc array of string=int and just does things like hasFlags, addFlags, removeFlags. A child class provide methods for working with the Active Directory field userAccountControl, which is a bitmask. It's created by returning the parent package with the known userAccountControl flags. I have methods like isDisabled, which simply checks to see if bit 2 is set.

I have replicated the the functionality of the base class (http://play.golang.org/p/8jtSZrDYwK), but I don't know how to "extend" this to a specific implementation with additional methods. I do realize the whole "parent class" and "child class" language does not apply to Go, but that's my question -- how do I think about this problem in Go, and what is the Go way of solving it?

If Go did inheritance, I would do is something like

// "parent" class
package bitmask

type Decoder struct {
   Flags map[string]int
}

func NewDecoder(bits map[string]int) *Decoder {
    return &Decoder{Flags: bits}
}

// other convenience methods

Then extend that as

// "child" class
package uac

import "bitmask" 

type UacDecoder Bitmask

func NewUacDecoder() *UacDecoder {
    bits := map[string]int
    bits["NORMAL_ACCOUNT"] = 512
    bits["ACCOUNTDISABLE"] = 2
    ...
    return &UacDecoder(Decoder(bits))
}

// This method is only valid with a certain set of
// well-known bits in a particular context (AD), so it 
// doesn't belong with the bitmask "parent" class 
func (d *UacDecoder) isDisabled(b int) bool {
    return b & d.Flags
}


Uli Kunitz

unread,
Nov 28, 2015, 2:02:31 PM11/28/15
to golang-nuts
Richard,

you need to understand how to compose types by struct embedding using anonymous fields.


Section 6.3 of the "The Go Programming Language" book by Alan Donovan and Brian Kernighan explains it too.

Uli

Egon

unread,
Nov 28, 2015, 2:18:51 PM11/28/15
to golang-nuts
As a general solution, how to figure out which types you need is to first implement solutions by copy pasting code. Sometimes this ends up being more readable than with any reuse.

Once you have 3 or more different implementations, then you can figure out the commonality. Now there are several ways to dedupe the common part.

1. make the functions work on a common data structure. e.g. in define package bitmask functions that take map[string]int as argument.

2. make a type, as you did, but manually forward the appropriate methods by casting. This is nice way to reduce the API, if you don't need a lot.

3. instead of renaming the type, define the uac thing as a struct and embed the bitmask. Of course this makes some creating code more annoying.

With the limited amount of information you showed, I would duplicate the code... it will be easier to manage.

Also for the disabled etc... flags I would create an enum instead of using numbers directly.

+ Egon

Richard Sugg

unread,
Nov 28, 2015, 4:54:45 PM11/28/15
to golang-nuts
Thanks for the responses -- I realized last night how to to do it -- composition. A few weeks ago, I took a class through ArdanLabs (Bill Kennedy was the instructor -- excellent class). While looking around, I suddenly remembered what he said about composition and type promotion. I typed up what I did, using the concept of User and Admin (an Admin is a User...) since that's a little easier to relate to than bitmasks: https://gist.github.com/rsperl/01d7a4da7c01706d50f6

Richard Sugg

unread,
Nov 28, 2015, 4:55:47 PM11/28/15
to golang-nuts
exactly -- funny thing is I have this book, but I'm only on chapter 4 :)  

rogerjd

unread,
Nov 28, 2015, 6:46:04 PM11/28/15
to golang-nuts
Methods of embedded types can also be overridden.
Roger

Tong Sun

unread,
Nov 29, 2015, 10:36:33 AM11/29/15
to golang-nuts


On Saturday, November 28, 2015 at 4:54:45 PM UTC-5, Richard Sugg wrote:
...A few weeks ago, I took a class through ArdanLabs (Bill Kennedy was the instructor -- excellent class). While looking around, I suddenly remembered what he said about composition and type promotion. I typed up what I did, using the concept of User and Admin (an Admin is a User...) since that's a little easier to relate to than bitmasks: https://gist.github.com/rsperl/01d7a4da7c01706d50f6

Thanks for sharing. 

Anybody knows the advantage of using 

 type Admin struct {
*User
SecretPassword string
}

over,

type Admin struct {
User
SecretPassword string
}

I.e., why use "*User" instead of "User" in the Admin struct?

Thanks

BTW, if anyone want to try it out, I've turn it into a full run-able program at 
and there are more demos there on embedding using anonymous fields too. 

Tim K

unread,
Nov 29, 2015, 10:37:00 PM11/29/15
to golang-nuts
Check out this blog post about embedding by value, by pointer and embedding interfaces:
http://www.hydrogen18.com/blog/golang-embedding.html

I too would like to hear of more use cases and example for embedding by pointer (and interface too).

Egon

unread,
Nov 30, 2015, 3:27:45 AM11/30/15
to golang-nuts
It mostly has to do with sharing information vs. copying information. Sometimes, with convenience.

1. You want the embedded item be shared between different items. One of those examples could be a struct that contains errors, and the individual members add errors to it. For example:

type Errors []error
func (errs *Errors) Add(err error) { *errs = append(*errs, err) }

type Parser struct {
   File string
   *Errors
}

When you recursively invoke the parser, the "file" part can change, but the errors list is the same, accumulating the different issues.

Of course, you get all the possible issues as "action at a distance".

2. You want to embed a large piece of read-only information. For example a in-memory index to some larger data-structure.

type Index struct { Data [1<<10]byte }
func (index *Index) Lookup(name string) []byte { ... }

Although, I have hard time figuring out a real-world example, how exactly would you do that!

Also, when you are wrapping a large structure, you would need to copy all the data if it isn't a pointer.

3. Sometimes it's nicer not having to dereference / take the address.

Parser{NewReader()}
vs. 
Parser{*NewReader()}

If your function returns a pointer it will look nicer if you put it into a pointer field.

+ Egon

Haddock

unread,
Nov 30, 2015, 2:08:17 PM11/30/15
to golang-nuts
Reply all
Reply to author
Forward
0 new messages