【问题标题】:Switch interface implementation without overhead无开销的切换接口实现
【发布时间】:2013-01-29 22:29:12
【问题描述】:

给定一个接口和两个(或更多)实现,我很难在扩展功能时轻松切换实现。

例如假设有接口 INumber 支持 Inc 和 String 以及两个实现 NumberInt32 和 NumberInt64 以及它们的明显实现。假设我想在 INumber 之上实现一个 EvenCounter。 EvenCounter 只有一个 IncTwice 并且应该调用 Inc 两次。我很难在不使用 EvenCounter 中 INumber 周围的额外结构的情况下获得正确的类型。

type INumber interface {
    Inc() 
    String() string
}

type NumberInt32 struct {
    number int32
}

func NewNumberInt32() INumber {
    ret := new(NumberInt32)
    ret.number = 0
    return ret
}

func (this *NumberInt32) Inc() { 
    this.number += 1
}

func (this *NumberInt32) String() string {
    return fmt.Sprintf("%d", this.number)
}

// type NumberInt64.... // obvious

这是我挣扎的地方

type EvenCounter1 INumber // nope, additional methods not possible 
type EvenCounter2 NumberInt32 // nope
func (this *EvenCounter2) IncTwice() {  
for i:=0; i < 2; i+=1 {
    // this.Inc() // Inc not found
    // INumber(*this).Inc() // cannot convert       
    // in, ok := *this.(INumber) // cannot convert
    // v, ok := this.(INumber) // cannot convert
    // a concrete conversion a) does not work and b) won't help
    // here it should be generic
    // v, ok := this.(NumberInt32) 
    // How do I call Inc here on this?
    }
}

只需嵌入到结构中即可...

type EvenCounter3 struct {
    n INumber
}

func (this *EvenCounter3) IncTwice() {
    n := this.n // that is a step I want to avoid
    n.Inc() // using this.n.Inc() twice makes it slower
    n.Inc()
}

func (this *EvenCounter3) String() string {
    return this.n.String()
}

我可以忍受为每种方法手动实现委托的需要,但是 显然我想依赖 INumber 而不是具体的实现(即 将意味着改变很多地方来尝试另一种实现,但是,我想避免额外的间接和(很可能?)额外的空间。有没有 避免结构并直接说 EvenCounter 是带有其他方法的(特定)INumber 的方法?

顺便说一句,真正的例子是一组整数和一个整数到整数的映射,数百万个实例都交织在一起(不,只是 map[int]bool 是不够的 - 太慢了,bitset 很有趣,取决于用例等)并通过更改代码中的 2-3 行轻松测试 set 和 map 的不同实现(理想情况下只是类型,也许 实例的通用创建。复制)

任何帮助表示赞赏,我希望这还没有被问到......

【问题讨论】:

    标签: types interface go implementation


    【解决方案1】:

    您使用嵌入的变体实际上并未嵌入。 Embedded fields are anonymous and Go then delegates automatically.

    这将您的示例简化为:

    type EvenCounter3 struct {
        INumber
    }
    
    func (this *EvenCounter3) IncTwice() {
        this.Inc() // using this.n.Inc() twice makes it slower
        this.Inc()
    }
    

    请注意,String() 是自动委托的(在 Go 中是“提升”)。

    至于调用 Inc() 两次使其变慢,嗯,这是使用接口的限制。接口的重点是不公开实现,因此您无法访问其内部数字变量。

    【讨论】:

    • 谢谢:好的,我读的是接口而不是结构。自动委托就可以了。我会继承 INumber 的所有公共方法吗?使用具体类型我可以隐藏它们吗?
    • 我不想访问 number 变量。 this.n.Inc() 与 this.Inc() 的问题是额外的间接 this.n - 而不是对实际方法的调用。带有匿名嵌入字段的语法感觉不错。但是,一个快速而肮脏的测试(在循环中进行)表明,在下面可能存在相同的间接性,因为我有与命名字段相同的惩罚。我想我必须忍受这一点。谢谢!
    • 如果您考虑接口的语义(特别是 Go):在所讨论的场景中,“双重”间接是不可避免的(当嵌入的“事物”是接口时,通过嵌入继承方法)。
    【解决方案2】:

    我不确定我是否正确理解了您想要实现的目标。也许是这样的?

    package main
    
    import "fmt"
    
    type Num interface {
            Inc()
            String() string
    }
    
    type Int32 struct {
            int32
    }
    
    func (n *Int32) Inc() {
            (*n).int32++
    }
    
    func (n Int32) String() string {
            return fmt.Sprintf("%d", n.int32)
    }
    
    type EventCounter interface {
            Num
            IncTwice()
    }
    
    type Event struct {
            Num
    }
    
    func (e Event) IncTwice() {
            e.Inc()
            e.Inc()
    }
    
    func main() {
            e := Event{&Int32{42}}
            e.IncTwice()
            fmt.Println(e)
    }
    

    (也称为here


    输出

    44
    

    【讨论】:

    • 谢谢:大致上,只是在Event 的定义中没有围绕num 的额外结构。但正如另一个答案指出的那样,如果没有给出名字,似乎会有一个自动委托。
    猜你喜欢
    • 2010-10-27
    • 1970-01-01
    • 2021-04-03
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    相关资源
    最近更新 更多