Multiple sets of const names with scope in Go

huangapple go评论67阅读模式
英文:

Multiple sets of const names with scope in Go

问题

我有一个需要无限数量常量集的Go应用程序。该应用程序还要求我能够在运行时将字符串映射到(整数)常量,反之亦然。常量的名称只保证是有效的标识符,因此很可能会有重复的常量名称。特别是,每个常量集都有一个名为"Invalid"的元素。在C++11中,我会使用枚举类来实现作用域。在Python中,我可能会使用类变量。我在寻找一种在Go中表达这个问题的惯用方式。我已经考虑过的选项包括:

  • 为每个常量集使用单独的包。这样做有很多缺点,因为我更希望整个集合在同一个包中,这样我就可以在包级别上构建对这些集合的支持,并且可以在不过度复杂化测试代码的情况下对整个集合进行测试。

first.go:

package first
type First int
const (
    ConstOne First = iota
    ConstTwo
    Invalid = -1
)

func IntToCode(fi First)string { ... }
func CodeToInt(code string)First { ... }

second.go:

package second
type Second int
const (
    ConstOne Second = iota
    ConstTwo
    Invalid = -1
)

func IntToCode(se Second)string { ... }
func CodeToInt(code string)Second { ... }

example.go:

import (
    "first"
    "second"
)

First fi = first.CodeToInt("ConstOne")
Second se = second.Invalid
  • 使用一种经过验证的技术,为每个常量设置全局唯一的前缀。然而,考虑到集合的数量非常大,使用编码约定来实质上发明和管理一堆命名空间是非常笨拙的。这个选项还迫使我修改常量的名称(这正是使用它们的目的)。

first.go:

package mypackage

type First int

const (
    FI_CONSTONE First = iota
    FI_CONSTTWO
    FI_INVALID = -1
)

func IntToCode(fi First)string { ... }
func CodeToInt(code string)First { ... }

second.go:

package mypackage

type Second int

const (
    SE_CONSTONE Second = iota
    SE_CONSTTWO
    SE_INVALID = -1
)

func IntToCode(se Second)string { ... }
func CodeToInt(code string)Second { ... }

example.go:

package mypackage

import (
    "first"
    "second"
)

First fi = first.CodeToInt("ConstOne")
Second se = SE_INVALID

有没有更好、更符合惯用方式的解决方案?我希望能够这样说:

First fi = First.Invalid

但是我还没有成功找到一种允许这样做的方法。

英文:

I have a Go application that requires an unbounded number of sets of constants. The application also requires that I be able to map strings to (integer) consts, and vice versa at runtime. The names of the constants are guaranteed only to be valid identifiers, so it's a near certainty that there will be duplicate const names. In particular, each set of constants has an element called "Invalid". In C++11, I'd use enum classes to achieve scope. In Python, I'd probably use class variables. I'm struggling to find an idiomatic way to express this in Go. Options that I've looked at include:

  • Using a separate package for every set of consts. This has lots of disadvantages, because I'd rather the whole set be in the same package so that I can build support for these sets at the package level, and so that I can test the whole lot without overly complicating the test code to do multi-package testing at once.

first.go:

package first
type First int
const (
    ConstOne First = iota
    ConstTwo
    Invalid = -1
)

func IntToCode(fi First)string { ... }
func CodeToInt(code string)First { ... }

second.go:

package second
type Second int
const (
    ConstOne Second = iota
    ConstTwo
    Invalid = -1
)

func IntToCode(se Second)string { ... }
func CodeToInt(code string)Second { ... }

example.go:

import (
    "first"
    "second"
)

First fi = first.CodeToInt("ConstOne")
Second se = second.Invalid
  • Using the tried-and-true technique of a globally unique prefix for each const. Given, though, that the number of sets is really big, having to essentially invent and manage a bunch of namespaces using a coding convention is awkward at best. This option also forces me to modify the names of the consts (which is the whole point of using them).

first.go:

package mypackage

type First int

const (
    FI_CONSTONE First = iota
    FI_CONSTTWO
    FI_INVALID = -1
)

func IntToCode(fi First)string { ... }
func CodeToInt(code string)First { ... }

second.go:

package mypackage

type Second int

const (
    SE_CONSTONE Second = iota
    SE_CONSTTWO
    SE_INVALID = -1
)

func IntToCode(se Second)string { ... }
func CodeToInt(code string)Second { ... }

example.go:

package mypackage

import (
    "first"
    "second"
)

First fi = first.CodeToInt("ConstOne")
Second se = SE_INVALID

What's a better, more idiomatic solution? I'd love to be able to say things like:

First fi = First.Invalid

but I haven't been successful in coming up with an approach that allows this.

答案1

得分: 0

你可以为不同的代码集创建单独的包,并为与代码相关的高级功能定义一个单独的包,并为该高级包编写测试。

例如,对于一个代码集:

package product // 或者与代码集相关的其他命名空间

type Const int

const (
    ONE Const = iota
    TWO
    ...
    INVALID = -1
)

func (c Const) Code() string {
    return intToCode(int(c)) // 内部实现
}

type Code string

const (
     STR_ONE Code = "ONE"
     ...
)

func (c Code) Const() int {
    return codeToInt(string(c)) // 内部实现
}

例如,对于高级功能函数包:

// codes.go
package codes

type Coder interface{
    Code() string
}

type Conster interface{
    Const() int
}

func DoSomething(c Coder) string {
    return "The code is: " + c.Code()
}    

// codes_product_test.go
package codes

import "product"

func TestProductCodes(t *testing.T) {
    got := DoSomething(product.ONE) // 你可以将product.Const作为Coder传递
    want := "The code is: ONE"
    if got != want {
        t.Error("got:", got, "want:", want)
    }
}

编辑:

要获取特定代码的常量,你可以这样做:

product.Code("STRCODE").Const()

也许更好的做法是Const()返回一个Coder,这样product.Code("ONE").Const()可以是一个product.Const。我认为如果你尝试一下,会有几个选项,其中肯定有一个好的选项。

英文:

You could have separate packages for your separate code sets and a single package for defining the higher level functionality associated with codes and write tests for that higher level package.

I haven't compiled or tested any of this:

E.g. for a code set:

package product // or whatever namespace is associated with the codeset

type Const int

const (
    ONE Const = iota
    TWO
    ...
    INVALID = -1
)

func (c Const) Code() string {
    return intToCode(int(c)) // internal implementation
}

type Code string

const (
     STR_ONE Code = "ONE"
     ...
)

func (c Code) Const() int {
    return codeToInt(string(c)) // internal implementation
}

E.g. for the higher level functions package:

// codes.go
package codes

type Coder interface{
    Code() string
}

type Conster interface{
    Const() int
}

func DoSomething(c Coder) string {
    return "The code is: " + c.Code()
}    

// codes_product_test.go
package codes

import "product"

func TestProductCodes(t *testing.T) {
    got := DoSomething(product.ONE) // you can pass a product.Const as a Coder
    want := "The code is: ONE"
    if got != want {
        t.Error("got:", got, "want:", want)
    }
}

Edit:

To retrieve the const for a particular code you could do something like

product.Code("STRCODE").Const()

Maybe it's better if Const() returns a Coder so that product.Code("ONE").Const() can be a product.Const. I think if you play around with it there are a few options and there will be a good one in there somewhere.

huangapple
  • 本文由 发表于 2015年12月29日 08:11:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/34502647.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定