dssu33392 2015-10-09 20:59
浏览 173
已采纳

Golang:代码重复和类似的结构

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)

  • 写回答

2条回答 默认 最新

  • dongwu3596 2015-10-11 11:00
    关注

    I don't understand your comment about embedding. Here is how I'd do it (using embedding):

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 MddBootstrapInitialize2失败
  • ¥15 LCD Flicker
  • ¥15 Spring MVC项目,访问不到相应的控制器方法
  • ¥15 esp32在micropython环境下使用ssl/tls连接mqtt服务器出现以下报错Connected on 192.168.154.223发生意外错误: 5无法连接到 MQTT 代理,如何解决?
  • ¥15 关于#genesiscsheel#的问题,如何解决?
  • ¥15 Android aidl for hal
  • ¥15 STM32CubeIDE下载程序报错
  • ¥15 微信好友如何转变为会员系统?(相关搜索:小程序)
  • ¥15 c# 直接使用c++ 类库文件
  • ¥15 一个主机电脑有两个显示器,当前有两个软件主体是网页html打包的exe程序,如何通过cmd命令bat同时打开软件一个在主屏幕显示,另外一个软件在2副屏幕上显示