When should a type be a struct containing another type and when should it just "extend"(?) that type?

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

When should a type be a struct containing another type and when should it just "extend"(?) that type?

问题

我目前正在通过完成rosalind问题(基本上是一堆与生物信息学相关的代码卡塔)来学习Go语言。

我目前使用一个类型来表示DNA链:

type DNAStrand struct {
    dna byte[]
}

我最初的目的是封装字节切片,以便我知道它只包含表示核苷酸的字节:'A', 'C', 'G', 'T'。但我意识到这显然不能保证,因为我可以简单地这样做:

DNAStrand{[]byte("foo bar")}

这样就无法保证我的DNA链包含一个只包含这四个字节元素的字节数组。

由于我的结构体只包含一个字节数组,所以更好/更符合惯例的做法是:

type DNAStrand []byte

还是让类型包含DNA链更好?在何时使用这两种方法是否有任何经验法则?

英文:

I'm currently learning Go by doing the rosalind problems (basically a bunch of bioinformatics related code katas).

I'm currently representing a DNA strand with a type:

type DNAStrand struct {
    dna byte[]
}

My initial reason was to encapsulate the byte slice so I would know it only contained bytes representing the nucleotides: 'A', 'C', 'G', 'T'. I realized that this was obviously not guarateed since I could simply do:

DNAStrand{[]byte("foo bar")}

And there is no longer any guarantee that my dna strand contains a byte array with only elements from those four bytes.

Since my struct only contains a byte array is it better/more ideomatic to do:

type DNAStrand []byte

Or is it better to let the type contain the dna strand? Are there any rules of thumb for when to use either of the two approaches?

答案1

得分: 11

type neucleotide char // unexported type users can't construct their own.

type DNAStrand []neucleotide // because users can't construct their own
// nucleotides they also can't construct their
// own DNAStrands.

const (
// These are exported values so they can use these nucleotides to construct a
// DNAStrand with.
A nucleotide = 'A'
C nucleotide = 'C'
G nudleotide = 'G'
T nucleotide = 'T'
)

// This function allows them to actually construct a DNAstrand with a list of
// nucleotides from the constants above.
func New(nts ...nucleotide) DNAStrand {
return nts
}

英文:

Taking your specific example I would probably do something like this:

type neucleotide char // unexported type users can't construct their own.

type DNAStrand []neucleotide // because users can't construct their own
                             // nucleotides they also can't construct their
                             // own DNAStrands.

const (
  // These are exported values so they can use these nucleotides to construct a
  // DNAStrand with.
  A nucleotide = 'A'
  C nucleotide = 'C'
  G nudleotide = 'G'
  T nucleotide = 'T'
)

// This function allows them to actually construct a DNAstrand with a list of
//  nucleotides from the constants above.
func New(nts ...nucleotide) DNAStrand {
    return nts
}

Since the nucleotide type is not exported users can't construct their own. You provide the only allowed instances of them in the exported consts so no user can provide their own new nucleotides.

答案2

得分: 2

具有零个字段的结构体很方便。具有许多字段的结构体更加方便。具有仅一个字段的结构体有点特殊,我无法想到一个合理的“好”用例来使用它们 - 即使它们经常在实际中看到。就我个人而言,我不使用它们。

无论如何,如果您确实需要更严格/防弹的安全性来处理DNAStrand切片内容 - 那么可以使用单字段结构体,并为此/此命名类型定义一个参数检查的setter方法。

在这种情况下,如果稍后从其他包中使用该定义,除非使用package unsafe,否则无法绕过检查并获得与您的DNAStrand{[]byte("foo bar")}示例等效的结果。

英文:

Struct with zero fields are handy. Structs with many fields are handy even more. Structs with exactly one field are a bit special and I can't think of a reasonably "good" case where to use them - even though they are seen regularly "in the wild". I, for one, don't use them.

Anyway, if you really really need tighter/bulletproof safety about the DNAStrand slice content - then it is possible to to use the single field struct and define an argument checking setter method for this/such named type.

In that case, if the definition is later used from some other package, there's no way, modulo using package unsafe, to circumvent the checks and get a result equivalent to your DNAStrand{[]byte("foo bar")} example.

答案3

得分: 1

我会使用type DNAStrand []byte,因为它简单,并且我可以在其上使用正则表达式。不过,我可能会使用一个初始化函数来检查每个字节是否在ACGT中。

var validDNAStrandPat = regexp.MustCompile("[ACTG]*")

func DNAStrandForString(s string) DNAStrand {
    if !validDNAStrandPat.Match(s) {
        panic("Invalid DNA Strand.")
    }
    return DNAStrand([]byte(s))
}
英文:

I'd use type DNAStrand []byte because it's simple, and because I can use regexps on it. I'd probably use an initialisation function that checks that every byte is in ACGT though.

var validDNAStrandPat = regexp.MustCompile("[ACTG]*")

func DNAStrandForString(s string) DNAStrand {
    if !validDNAStrandPat.Match(s) {
        panic("Invalid DNA Strand.")
    }
    return DNAStrand([]byte(s))
}

huangapple
  • 本文由 发表于 2013年1月9日 21:26:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/14236263.html
匿名

发表评论

匿名网友

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

确定