切换接口实现而无需额外开销

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

Switch interface implementation without overhead

问题

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

例如,假设有一个支持Inc和String的接口INumber,以及两个实现NumberInt32和NumberInt64及其明显的实现。假设我想在INumber之上实现一个EvenCounter。EvenCounter只有一个IncTwice方法,它将调用Inc两次。我很难在不使用额外的结构将INumber包装在EvenCounter中的情况下正确使用类型。

这是我遇到困难的地方:

type EvenCounter1 INumber // 不行,无法添加额外的方法
type EvenCounter2 NumberInt32 // 不行
func (this *EvenCounter2) IncTwice() {	
    for i:=0; i < 2; i+=1 {
        // this.Inc() // 找不到Inc
        // INumber(*this).Inc() // 无法转换
        // in, ok := *this.(INumber) // 无法转换
        // v, ok := this.(INumber) // 无法转换
        // 具体的转换 a) 不起作用,b) 不会有帮助
        // v, ok := this.(NumberInt32) 
        // 在这里如何调用Inc?
    }
}

只需嵌入一个结构即可解决问题:

type EvenCounter3 struct {
    n INumber
}

func (this *EvenCounter3) IncTwice() {
    n := this.n // 这是我想避免的一步
    n.Inc() // 使用this.n.Inc()两次会使其变慢
    n.Inc()
}

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

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

顺便说一下,真实的例子是一组整数和数百万个相互交织的整数到整数的映射(不,只有map[int]bool不够快,位集合根据用例是有趣的,等等),通过在代码中更改2-3行(理想情况下只是类型和可能的通用实例创建或复制)来轻松测试不同的集合和映射实现。

非常感谢任何帮助,希望这个问题还没有被问过...

英文:

Given an interface and two (or more) implementations I struggle to easily switch the implementation when extending the functionality.

For example assume that there is interface INumber which supports Inc and String and two implementations NumberInt32 and NumberInt64 with their obvious implementation. Assume that I want to implement an EvenCounter on top of INumber. The EvenCounter only has a IncTwice and shall call Inc twice. I struggle to get the types right without using an extra struct surrounding the INumber in EvenCounter.

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(&quot;%d&quot;, this.number)
}

// type NumberInt64.... // obvious

Here is where I struggle

type EvenCounter1 INumber // nope, additional methods not possible 
type EvenCounter2 NumberInt32 // nope
func (this *EvenCounter2) IncTwice() {	
for i:=0; i &lt; 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&#39;t help
	// here it should be generic
	// v, ok := this.(NumberInt32) 
	// How do I call Inc here on this?
    }
}

Just embedding in a struct works...

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()
}

I could live with the need to implement delegation manually for each method, but
obviously I would want to rely on INumber and not the specific implementation (that
would mean changing lots of places to try another implementation, However, I would want to avoid the extra indirection and (most likely?) extra space. Is there a
way to avoid the struct and directly say EvenCounter is a (specific) INumber with additional methods?

BTW the real example is a set of integers and a map of integers to integers with millions of instances all intertwined (and no, just map[int]bool won't suffice - too slow, bitset is interesting depening on use case, etc.) and testing different implementations of the set and map easily by changing 2-3 lines in the code (ideally just the type and maybe
the generic creation of instances resp. making copies)

Any help appreciated and I hope this hasn't been asked yet...

答案1

得分: 4

你的使用嵌入的变体实际上没有嵌入。嵌入字段是匿名的,Go会自动委托

这简化了你的示例:

type EvenCounter3 struct {
    INumber
}

func (this *EvenCounter3) IncTwice() {
    this.Inc() // 使用 this.n.Inc() 两次会使其变慢
    this.Inc()
}

请注意,String()会自动委托(在Go中称为“提升”)。

至于调用Inc()两次会使其变慢,这是使用接口的限制。接口的目的是不暴露实现,因此无法访问其内部的number变量。

英文:

Your variant using embedding does not actually embed. Embedded fields are anonymous and Go then delegates automatically.

This simplifies your example to:

type EvenCounter3 struct {
    INumber
}

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

Note that String() is automatically delegated ("promoted" in Go speak).

As to calling Inc() twice making it slower, well, that's a limitation of using interfaces. The point of an interface is to not expose the implementation, so you cannot access its internal number variable.

答案2

得分: 0

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)
}

英文:

I'm not sure I understood correctly what you're trying to achieve. Perhaps something like this?

package main

import &quot;fmt&quot;

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

type Int32 struct {
        int32
}

func (n *Int32) Inc() {
        (*n).int32++
}

func (n Int32) String() string {
        return fmt.Sprintf(&quot;%d&quot;, n.int32)
}

type EventCounter interface {
        Num
        IncTwice()
}

type Event struct {
        Num
}

func (e Event) IncTwice() {
        e.Inc()
        e.Inc()
}

func main() {
        e := Event{&amp;Int32{42}}
        e.IncTwice()
        fmt.Println(e)
}

(Alse here)


Output

44

huangapple
  • 本文由 发表于 2013年2月14日 15:01:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/14869440.html
匿名

发表评论

匿名网友

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

确定