为了最小化代码重复,Go的惯用方式是父类(super class)相似(但不相同)的数据类型吗?字幕示例:
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)
}
显然,这还不止于此,但我希望能够以某种方式对它们进行父类(super class),因此我只需要编写和维护一个特定功能的副本即可。接口(interface)似乎是“正确”的方式,但是有很多不足之处(无效示例):
type Utmp interface {
Time() time.Time
}
func User(u Utmp) string {
return string(u.ut_user[:])
}
我也考虑过嵌入,但是由于Go的类型非常严格,所以这似乎也是死路一条。我是否注定要有多个代码,除了签名外,其他所有代码都相同?
[编辑]
复杂的部分原因是我正在使用encoding/binary.Read()根据字节顺序分析此数据(不只是utmp记录,而不仅仅是Linux/BSD)。要使用该字段,必须按照它们在磁盘上的确切顺序将这些字段[导出]到结构中。因此,我不能只嵌入另一个结构的字段,因为在某些记录中它们的顺序不同(大小不同)
最佳答案
我不理解您对嵌入的评论。这是我的方法(使用嵌入):
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)
}
任何导入该库的代码都可以直接在
User()
和LinuxUtmp
对象上以BsdUtmp
或l.User()
的形式直接调用b.User()
方法,而根本不提及Utmp
。如果愿意,您甚至可以使Utmp
保持意外状态(如utmp
)。有关详细信息,请查看Effective Go。
如果愿意,您甚至可以确保仅将用于相关平台的代码编译为二进制文件。 This blog有一些示例。为了使事情保持简单,如果平台特定的代码不是很大或涉及其他因素,我将不走这条路。
为了完整起见,这里是官方的go build文档。