Go的工会最佳实践

时间:2014-02-04 13:06:22

标签: go unions

Go has no unions。但工会在许多地方都是必要的。 XML过度使用了联合或选择类型。我试图找出,这是解决失踪工会的首选方法。作为示例,我尝试在XML standard中为非终端Misc编写Go代码,该代码可以是commentprocessing instructionwhite space。< / p>

编写三种基本类型的代码非常简单。它们映射到字符数组和结构。

type Comment Chars

type ProcessingInstruction struct {
    Target *Chars
    Data *Chars
}

type WhiteSpace Chars

但是当我完成了联合的代码时,它有很多冗余函数。显然必须有一个容器结构。

type Misc struct {
    value interface {}
}

为了确保容器只保存三个允许的类型,我将值设为私有,我必须为每个类型编写一个构造函数。

func MiscComment(c *Comment) *Misc {
    return &Misc{c}
}

func MiscProcessingInstruction (pi *ProcessingInstruction) *Misc {
    return &Misc{pi}
}

func MiscWhiteSpace (ws *WhiteSpace) *Misc {
    return &Misc{ws}
}

为了能够测试联合的内容,有必要编写三个谓词:

func (m Misc) IsComment () bool {
    _, itis := m.value.(*Comment)
    return itis
}

func (m Misc) IsProcessingInstruction () bool {
    _, itis := m.value.(*ProcessingInstruction)
    return itis
}

func (m Misc) IsWhiteSpace () bool {
    _, itis := m.value.(*WhiteSpace)
    return itis
}

为了获得正确输入的元素,有必要编写三个getter。

func (m Misc) Comment () *Comment {
    return m.value.(*Comment)
}

func (m Misc) ProcessingInstruction () *ProcessingInstruction {
    return m.value.(*ProcessingInstruction)
}

func (m Misc) WhiteSpace () *WhiteSpace {
    return m.value.(*WhiteSpace)
}

在此之后,我能够创建一个Misc类型的数组并使用它:

func main () {

    miscs := []*Misc{
        MiscComment((*Comment)(NewChars("comment"))),
        MiscProcessingInstruction(&ProcessingInstruction{
            NewChars("target"),
            NewChars("data")}),
        MiscWhiteSpace((*WhiteSpace)(NewChars(" \n")))}

    for _, misc := range miscs {
        if (misc.IsComment()) {
            fmt.Println ((*Chars)(misc.Comment()))
        } else if (misc.IsProcessingInstruction()) {
            fmt.Println (*misc.ProcessingInstruction())
        } else if (misc.IsWhiteSpace()) {
            fmt.Println ((*Chars)(misc.WhiteSpace()))
        } else {
            panic ("invalid misc");
        }
    }
}

你看到有很多代码看起来几乎一样。任何其他工会都会如此。所以我的问题是:有没有办法简化在Go中处理工会的方式?

通过removing redundancy来简化编程工作。但我认为上面的例子显示了完全相反的情况。我错过了什么吗?

以下是完整示例:http://play.golang.org/p/Zv8rYX-aFr

4 个答案:

答案 0 :(得分:8)

因为你似乎要问因为你想要类型安全,我首先会说你的初始 解决方案已经不安全了,因为你有

func (m Misc) Comment () *Comment {
    return m.value.(*Comment)
}
如果你之前没有检查IsComment,那么会发生恐慌。因此,这种解决方案没有任何好处 Volker提出的类型切换。

由于您要对代码进行分组,因此您可以编写一个函数来确定Misc元素是什么:

func IsMisc(v {}interface) bool {
    switch v.(type) {
        case Comment: return true
        // ...
    }
}

然而,这将使您无法进行编译器类型检查。

如果您希望能够通过编译器将某些内容标识为Misc,那么您应该这样做 考虑创建一个标记为Misc的接口:

type Misc interface {
    ImplementsMisc()
}

type Comment Chars
func (c Comment) ImplementsMisc() {}

type ProcessingInstruction
func (p ProcessingInstruction) ImplementsMisc() {}

这样你就可以编写只处理misc的函数。对象并稍后决定 你真正想要处理的事情(评论,指示......)。

如果你想模仿工会,那么你所写的方式就是我所知道的方式。

答案 1 :(得分:6)

我认为这些代码可能会减少,例如我个人认为保护type Misc不包含“非法”内容真的很有帮助:一个简单的type Misc interface{}会做什么,或者?

这样你就可以省去构造函数,所有Is{Comment,ProcessingInstruction,WhiteSpace}方法都归结为类型开关

switch m := misc.(type) {
    Comment: fmt.Println(m)
    ... 
    default: panic()
}

这是什么包编码/ xml用Token做的。

答案 2 :(得分:1)

我不确定你理解你的问题。 “简单”的方法就像带有接口{}的encoding / xml包。如果您不想使用接口,那么您可以像您一样做。 但是,正如您所说,Go是一种打字语言,因此应该用于打字需求。 如果你有一个结构化的XML,Go可以很合适,但你需要编写你的模式。如果你想要一个可变参数模式(一个给定的字段可以有多种类型),那么使用非类型语言可能会更好。

非常有用的json工具,可以轻松地为xml重写: http://mholt.github.io/json-to-go/

您提供了一个json输入,它为您提供了精确的Go结构。您可以有多种类型,但您需要知道哪个字段具有哪种类型。如果你不这样做,你需要使用反射,实际上你失去了Go的许多兴趣。

答案 3 :(得分:-1)

TL; DR 您不需要工会,interface{}可以更好地解决此问题。

C中的联合用于访问特殊内存/硬件。他们还颠覆类型系统。 Go没有语言原语访问特殊内存/硬件,它也出于同样的原因避开了volatile和位字段。

在C / C ++中,联合会也可以用于真正的低级优化/比特打包。权衡:牺牲类型系统并增加复杂性,有利于节省一些比特。这当然伴随着关于优化的所有警告。

想象一下Go有一个原生的联合类型。代码怎么会更好?用这个重写代码:

// pretend this struct was a union
type MiscUnion struct {
  c *Comment
  pi *ProcessingInstruction
  ws *WhiteSpace
}

即使使用内置联盟访问MiscUnion的成员,也需要进行某种运行时检查。所以使用界面并没有变得更糟。可以说interface是优越的,因为运行时类型检查是内置的(不可能出错)并且具有非常好的语法来处理它。

联合类型的一个优点是静态类型检查,以确保只有适当的具体类型放在Misc中。解决这个问题的Go方法是“New ...”函数,例如MiscCommentMiscProcessingInstructionMiscWhiteSpace

以下是使用interface{}New*函数的清理示例:http://play.golang.org/p/d5bC8mZAB_