英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论