

Struct composition with Go - jcxplorer
http://dave.cheney.net/2015/05/22/struct-composition-with-go

======
zenocon
This is cool, but I find this snippet a tad bizarre:

    
    
      client, server := net.Pipe()
            var buf bytes.Buffer
            client = &recordingConn {
                    Conn: client,
                    Writer: io.MultiWriter(client, &buf),
            }
    

While it seemingly works, it seems confusing to assign client to an internal
field of a new struct, and then overwriting that same client variable with the
newly created struct...not to mention passing it into MultiWriter.

Anyway, I like those short posts like this. I've done similar things with
embedding and struct "reconstruction" in order to elicit more testable code.
It's powerful and useful, indeed.

~~~
gohrt
What is the benefit of re-using the 'client' identifier? There's no closure
capturing the identifier.

Compare:

    
    
            client, server := net.Pipe()
            var buf bytes.Buffer
            client = &recordingConn {
                    Conn: client,
                    Writer: io.MultiWriter(client, &buf),
            }
    

vs

    
    
            tempClient, server := net.Pipe()
            var buf bytes.Buffer
            client = &recordingConn {
                    Conn: tempClient,
                    Writer: io.MultiWriter(tempClient, &buf),
            }
    

The latter is less confusing to me.

------
aleksi
In the end there is no need to embed (exported) io.Writed, it should be
replaced with non-exported non-anonymous field.

------
shoo
Since the purpose of this is apparently to provide a fake structure to test
against - would another viable approach have been to implement a trivial
`Close` function that doesn't do anything?

(context: i am no gopher)

(edit/tangent: i wonder how method calls from inside method calls are resolved
when the names are ambiguous?)

~~~
Sphax
You need quite a few more method to be a net.Conn actually:
[http://golang.org/pkg/net/#Conn](http://golang.org/pkg/net/#Conn). Easier to
just overwrite the Write() method like this in that case.

------
justinsb
Is it reasonable to think of this as multiple inheritance?

~~~
davecheney
Nope, this is composition, not inheritance.

~~~
justinsb
It looks more like inheritance: the methods on net.Conn & io.Writer are
automatically exposed. Are you saying that it is composition because you are
"inheriting" from interfaces, which isn't traditional inheritance? Or if not,
can you explain why you think of this as composition and not inheritance?

Edit: On further thought, this looks to me like composition with automatic
delegation (a huge productivity benefit of inheritance). It is unusual because
the combined class also implements the interfaces because of Go's interface
rules, but I think that the late-binding to implementations is more similar to
composition.

~~~
jaekwon
The inner structs (of which the outer writer struct is composed) cannot access
any of the functionality or fields outside of itself. Each inner struct is
completely encapsulated, so you can't override an inner struct method and
expect the inner struct's behavior to change.

The accessibility of inner struct methods from the outer struct is a syntactic
convenience.

Java:
[https://gist.github.com/jaekwon/8025b9f3a482b3219a21](https://gist.github.com/jaekwon/8025b9f3a482b3219a21)
Go:
[https://gist.github.com/jaekwon/0f6e5555ab6a592aa4c8](https://gist.github.com/jaekwon/0f6e5555ab6a592aa4c8)

Once you Go, you never go back. ;)

~~~
pcwalton
There are also no virtual methods with struct composition. If B embeds A,
"upcasting" an instance of B to A will not allow you to call any methods on B.

