英文:
Golang: code duplication and similar structs
问题
在Go语言中,可以使用接口来实现类似但不完全相同的数据类型的上层类型,以减少代码重复。以下是一个简单的示例:
import "time"
type Utmp interface {
User() string
Time() time.Time
}
type LinuxUtmp struct {
ut_type uint16
_ [2]byte
ut_pid uint32
ut_line [32]byte
ut_id [4]byte
ut_user [32]byte
ut_host [256]byte
exit_status [2]uint32
tv_sec uint32
tv_usec uint32
// ...
}
func (l LinuxUtmp) User() string {
return string(l.ut_user[:])
}
func (l LinuxUtmp) Time() time.Time {
return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}
type BsdUtmp struct {
ut_line [8]byte
ut_name [16]byte
ut_host [16]byte
ut_time uint32
}
func (b BsdUtmp) User() string {
return string(b.ut_user[:])
}
func (b BsdUtmp) Time() time.Time {
return time.Unix(int64(b.ut_time), 0)
}
通过定义一个Utmp
接口,你可以将LinuxUtmp
和BsdUtmp
类型都作为Utmp
类型的实现。这样,你就可以使用Utmp
类型的变量来调用User()
和Time()
方法,而无需关心具体的实现类型。
请注意,这只是一个简单的示例,实际情况可能更加复杂。你可能需要根据具体的需求来定义接口和方法,并在实现类型中提供相应的功能。
英文:
What's an idiomatic way in Go to superclass similar (but not identical) data types in order to minimize code duplication? Trite example:
import "time"
type LinuxUtmp struct {
ut_type uint16
_ [2]byte
ut_pid uint32
ut_line [32]byte
ut_id [4]byte
ut_user [32]byte
ut_host [256]byte
exit_status [2]uint32
tv_sec uint32
tv_usec uint32
...
}
func (l LinuxUtmp) User() string {
return string(l.ut_user[:])
}
func (l LinuxUtmp) Time() time.Time {
return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}
type BsdUtmp struct {
ut_line [8]char
ut_name [16]char
ut_host [16]char
ut_time uint32
}
func (b BsdUtmp) User() string {
return string(b.ut_user[:])
}
func (b BsdUtmp) Time() time.Time {
return time.Unix(int64(b.ut_time), 0)
}
Obviously there's more to it than that, but I'd love to be able to somehow superclass those so I only have to write and maintain one copy of particular functions. An interface seems to be the "right" way, but that leaves much to be desired (non-working example):
type Utmp interface {
Time() time.Time
}
func User(u Utmp) string {
return string(u.ut_user[:])
}
I've also considered embedding, but that seems a dead end too since Go is so strictly typed. Am I doomed to have multiple pieces of code that are identical in every way but the signature?
[edit]
Part of the complication is that I'm using encoding/binary.Read() to parse this data according to endianness (it's not just utmp records and not just Linux/BSD). To use that, the fields must be [exported] in the struct in the precise order they are on-disk. Hence I can't just embed the fields of another struct, as in some records they're in different order (and of differing sizes)
答案1
得分: 0
如果你遇到一些事物类型不同的问题,你可以让Time
和User
函数操作一个封装了Linux和BSD功能的接口。
如果你不喜欢这样做,你可以生成代码来避免重复。
英文:
If you have the problem that the types of some things are different, you could make the Time
and User
functions operate on an interface that wraps linux and bsd functionality.
If you don't like that, you can generate the code to avoid duplication.
答案2
得分: 0
我不理解你关于嵌入的评论。以下是我如何做(使用嵌入):
package test
import "time"
type Utmp struct {
// 公共字段
}
func (u Utmp) User() {
return string(l.ut_user[:])
}
type LinuxUtmp struct {
Utmp
// Linux 特定字段
}
func (l LinuxUtmp) Time() time.Time {
return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}
type BsdUtmp struct {
Utmp
// BSD 特定字段
}
func (b BsdUtmp) Time() time.Time {
return time.Unix(int64(b.ut_time), 0)
}
任何导入该库的代码都可以直接在 LinuxUtmp
和 BsdUtmp
对象上调用 User()
方法,如 l.User()
或 b.User()
,而无需提及 Utmp
。如果你喜欢,甚至可以将 Utmp
命名为 utmp
。
详细信息请参阅 Effective Go。
如果你愿意,甚至可以确保只有针对相关平台的代码被编译到二进制文件中。这篇博客 提供了一些示例。为了保持简单,如果平台特定的代码不是很大或涉及其他因素,我不会选择这种方法。
为了完整起见,这里是官方的 go build 文档。
英文:
I don't understand your comment about embedding. Here is how I'd do it (using embedding):
<!-- language: lang-golang -->
package test
import "time"
type Utmp struct {
// Common fields
}
func (u Utmp) User() {
return string(l.ut_user[:])
}
type LinuxUtmp struct {
Utmp
// Linux specific fields
}
func (l LinuxUtmp) Time() time.Time {
return time.Unix(int64(l.tv_sec), int64(l.tv_usec))
}
type BsdUtmp struct {
Utmp
// BSD specific fields
}
func (b BsdUtmp) Time() time.Time {
return time.Unix(int64(b.ut_time), 0)
}
Any code importing the library can call User()
method directly on LinuxUtmp
and BsdUtmp
objects directly as l.User()
or b.User()
without mentioning Utmp
at all. You can even keep Utmp
unexpected (as utmp
) if you like.
Check out Effective Go for details.
You can even ensure that only the code meant for the relevant platform gets compiled in the binary, if you like. This blog has got some examples. In the interest of keeping things simple, I wouldn't bother going down that route if the platform specific code is not very big or there are other factors involved.
For the sake of completeness, here is the official go build doc.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论