【问题标题】:When should a type be a struct containing another type and when should it just "extend"(?) that type?什么时候一个类型应该是一个包含另一种类型的结构,什么时候它应该只是“扩展”(?)那个类型?
【发布时间】:2012-12-23 13:29:09
【问题描述】:

我目前正在通过rosalind problems(基本上是一堆与生物信息学相关的代码katas)来学习Go。

我目前正在用一种类型表示一条 DNA 链:

type DNAStrand struct {
    dna byte[]
}

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

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

并且不再有任何保证我的 dna 链包含一个字节数组,其中仅包含这四个字节中的元素。

由于我的结构只包含一个字节数组,这样做是否更好/更符合思想:

type DNAStrand []byte

还是让类型包含 dna 链更好?对于何时使用这两种方法中的任何一种,是否有任何经验法则?

【问题讨论】:

    标签: types struct go


    【解决方案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))
    }
    

    【讨论】:

      【解决方案2】:

      以您的具体示例为例,我可能会这样做:

      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
      }
      

      由于核苷酸类型未导出,用户无法自己构建。您在导出的 const 中提供了它们唯一允许的实例,因此没有用户可以提供自己的新核苷酸。

      【讨论】:

      • 我最初是这样开始的,但是当我想要一个核苷酸能够给我它的赞美时就卡住了 (A <-> T, C <-> G)。现在我对未导出的类型有了更多的了解,我想我可以让核苷酸成为一个未导出的接口并为每个核苷酸创建未导出的类型......然后正如你所展示的那样将它们公开为常量。可能有点矫枉过正,但我​​喜欢为所有东西都有类型 :) Kudos!
      • 更进一步,如果 DNAStrands 的数量会很大并造成内存问题,您可以不以 ASCII 形式对它们进行编码,而是使用 Huffman 编码以压缩形式进行编码 - en.wikipedia.org/wiki/Huffman_coding -我为 SmallHand 类型执行此操作(在纸牌游戏中以跟踪手中的牌)-github.com/mzimmerman/sdzpinochle/blob/master/pinochle.go#L91
      【解决方案3】:

      零字段的结构很方便。具有许多字段的结构更加方便。仅具有一个字段的结构有点特殊,我想不出一个合理的“好”情况来使用它们——即使它们经常“在野外”看到。一方面,我不使用它们。

      无论如何,如果您真的需要对 DNAStrand 切片内容更严格/防弹的安全性 - 那么可以使用单字段结构并为此/此类命名类型定义参数检查设置器方法。

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

      【讨论】:

      • 但是难道不能从另一个包中执行DNAStrand{[]byte("foo bar")} 吗?或者我不能从它所在的包之外创建具有非导出字段(如DNAStrand)的结构吗?
      • 是的,这可能的。我应该更准确地说:...那么就没有意外方法来规避事情了...v := DNAStrand{[]byte("foo bar")} 至少对我来说是有意的情况 将该值分配给实例。相比之下,v.Set(foo())foo 中的错误返回“非法”值。
      • @jnml 我现在无法测试,但我认为尝试直接将值分配给来自不同包的未导出字段是错误的(除非有编译器错误或技巧)跨度>
      • @PaoloFalabella:现在我很困惑^2。这个例子play.golang.org/p/VU3yh_45Xj 让我想到了我在上面的第一条评论中写的内容,但这些字段实际上并不是未导出。真可惜,我现在无法从头顶弄清楚什么是正确的,所以我应该用代码检查它:-(
      • 现在尝试了一个简单的案例并得到以下错误:implicit assignment of unexported field 'a' in a.A literal。所以本质上在结构中有一个未导出的字段可以保护它不被从该包之外创建......
      猜你喜欢
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多