简体   繁体   中英

Go equivalent of type inheritace (gob encode/decode via interfaces)

I'm trying to write one function that encodes/decodes various types of Messages.

In OO languages, I would use type inheritance, but Go doesn't have this concept, ref: http://golang.org/doc/faq#inheritance , so instead, here I'm trying to use the "marker interface" style to leverage interface inheritance for this purpose instead.

The error is from golang org src/encoding/gob/decode.go:

   line 1019// Common confusing case: local interface type, remote concrete type.

.. and yes I find this confusing!!

Run this at https://play.golang.org/p/ldEvQXIgEa

I cannot get this to work, what am I doing wrong? Help please!

updates:

(I also tried to gob.Register(m1) with no effect).

I've looked into type embedding, but it doesn't do the thing that I am trying to do, as you can see in this example (that I've modified from jcbwlkr's code): https //play .golang .org/p/ P6AwVQqQcM (also be aware that I might have dozens/hundreds of message types!)

It is quite possible that I'm barking up the wrong tree, and that I need to ditch the whole idea of how I'm thinking about the problem (but this is what I'm trying to do at the moment: learn how to "Think in Go" vs OO thinking). Using type embedding would work, but I would then have to create a master type that embedded (composed) all of the possible types within it: gob makes the decoding possible, and a type switch lets me see what I got, but this all feels quite scary and uncomfortable to think about .

(I am used to static and dynamic languages, so I'm happy with duck typing, but this feels like a half-way house without the comforts of either end of the spectrum !).

So there are 2 things that I'm asking here:

  1. How to fix this particular problem (ie understand what's possible with interfaces), and
  2. design alternatives (and type embedding is one of those, so thanks for highlighting that) :) .

Here;s the broken code:

package main

import (
    "fmt"
    "bytes"
    "log"
    "encoding/gob"
)

type Msger interface {
    IsMsg()
}

type ClientMsg struct {
    Id      string
}
type ServerMsg struct {
    Id      string
}

func (m ClientMsg) IsMsg() { }
func (m ServerMsg) IsMsg() { }

func encode(m Msger) (bb bytes.Buffer) {
    enc := gob.NewEncoder(&bb)
    err := enc.Encode(m)
    if err != nil {
        log.Fatal("Cannot encode! err=", err)
    }
    return
}

func decode(m Msger, bb bytes.Buffer) {     // for A
//func decode(m *Msger, bb bytes.Buffer) {  // for B, C
    dec := gob.NewDecoder(&bb)
    err := dec.Decode(&m)
    if err != nil {
        log.Fatal("Cannot decode Msg! err=", err)
    }
    return
}

func main() {
    m1 := ClientMsg{"id_1"}
    b1 := encode(m1)
    p_bb := bytes.NewBuffer(b1.Bytes())

    var mDecoded ClientMsg // for A, B, C
    //var mDecoded interface{} // D: : cannot use mDecoded (type interface {}) as type Msger in argument to decode: interface {} does not implement Msger (missing IsMsg method)
    decode(mDecoded, *p_bb)     // A: gives: Cannot decode Msg! err=gob: local interface type *main.Msger can only be decoded from remote interface type; received concrete type ClientMsg = struct { Id string; }

    //decode(mDecoded, *p_bb)  // B: gives: cannot use mDecoded (type ClientMsg) as type *Msger in argument to decode: *Msger is pointer to interface, not interface
    //decode(&mDecoded, *p_bb) // C: gives: cannot use &mDecoded (type *ClientMsg) as type *Msger in argument to decode: *Msger is pointer to interface, not interface

    fmt.Printf("m1 Decoded='%v'\n", mDecoded)
}

Instead of using marker interfaces I would take an approach using Type Embedding and put the encode/decode methods on the Msg type.

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
    "log"
)

type Msg struct {
    Id string
}

type ClientMsg struct {
    Msg
}
type ServerMsg struct {
    Msg
}

func (m *Msg) encode() (bb bytes.Buffer) {
    enc := gob.NewEncoder(&bb)
    err := enc.Encode(m)
    if err != nil {
        log.Fatal("Cannot encode! err=", err)
    }
    return
}

func (m *Msg) decode(bb *bytes.Buffer) {
    dec := gob.NewDecoder(bb)
    err := dec.Decode(m)
    if err != nil {
        log.Fatal("Cannot decode Msg! err=", err)
    }
    return
}

func main() {
    // Make a message and encode it to a bytes.Buffer
    m1 := &ClientMsg{Msg{"id_1"}}
    b1 := m1.encode()
    p_bb := bytes.NewBuffer(b1.Bytes())

    // Make a new message and decode the bytes.Buffer from the first
    m2 := &ClientMsg{Msg{}}
    m2.decode(p_bb)

    fmt.Printf("m2 Decoded = '%v'\n", m2.Msg.Id)
}

View it on the Playground

You could make this code nicer with some New methods to make ClientMsg or ServerMsg structs. Also do you need to be passing the return from encode to bytes.NewBuffer or could you use it as is?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM